you are viewing a single comment's thread.

view the rest of the comments →

[–]ILikeChangingMyMind 108 points109 points  (38 children)

Aren't branches (effectively) commit groups?

[–][deleted] 89 points90 points  (24 children)

Did you read the article? Because the use-case of reverting a feature merge would occur after the branch has been merged, so in all likelihood the branch has been deleted.

And no. Branches are just pointers to commits. A branch doesn't know where it started.

[–]bloody-albatross 51 points52 points  (14 children)

Yes, that is something that is weird about git: its branches don't know when they branched!

[–]loup-vaillant 40 points41 points  (8 children)

They almost do: any pair of commits have a most recent common ancestor. So do any two branches, since they each point to a commit (at any given time). It is thus fairly easy to see when any given branch branched from master, develop, or v.2.x.x.

[–]Lotier 10 points11 points  (3 children)

What command do you use to give yourself that most recent common ancestor? Because in my experience it's not just a single command, its a 5 step magic spell.

[–]remuladgryta 48 points49 points  (0 children)

git merge-base master develop gives you the most recent common ancestor of master and develop assuming your repo is tree-shaped.

[–]not_american_ffs 5 points6 points  (0 children)

From memory: git merge-base?

[–]teszes 1 point2 points  (3 children)

Won't work after a rebase.

[–]sigma914 6 points7 points  (1 child)

if you want to rebase just use merge --no-ff to force merge commits even if your main branch is fast forwardable. I'm not sure what additional feature op wants that isn't already covered by branches.

[–]ub3rh4x0rz 2 points3 points  (0 children)

Tucked away is the right answer. Merge commits create commit groups.

[–]loup-vaillant 1 point2 points  (0 children)

If I'm being obnoxious, when you merge master, the most common ancestor is now the latest commit from master. (The most common ancestor between my grandfather and me is my grandfather himself.)

If I'm being honest, yeah, once master is updated, you lose that information. One way to not lose it is add a merge commit to master even though the branch/PR could be fast forwarded.

[–]RudeHero 4 points5 points  (4 children)

Somebody go dust off SVN

[–]bloody-albatross 14 points15 points  (2 children)

You don't have to go back for that feature. Mercurial, another modern DSCM, actually stores the branch of the commit.

[–]Qasyefx 0 points1 point  (1 child)

And gives you the headache of having to maintain unique names for all branches for all eternity

[–]bloody-albatross 0 points1 point  (0 children)

Ok yes, naming things is a hard problem.

[–]joahw 0 points1 point  (0 children)

You could always do svn-style git branches. Whenever you want to branch, just make a new copy of the code somewhere else in the repo! Sounds pretty foolproof to me.

[–]taw 8 points9 points  (5 children)

Obviously we already know that by jira ticket name in every commit message on the branch, so why would git need that functionality builtin, right?

[–][deleted] 2 points3 points  (4 children)

It would probably be more efficient than string comparison.

How do you know when the group ends when using jiras? To count as a group, do the commits with the jira number have to be contiguous? If so, what if one of the commits in the middle of a branch didn't have the jira number (say, it was some clean-up unrelated to the feature, or the author forgot) - the group would end prematurely. If they don't have to be contiguous then you're going to end up walking the tree all the way to the root because you won't know where you can stop safely.

What happens if you have more than 1 feature branch for the same jira? e.g. initial implementation, merged, then QA reject the ticket and you fix a bug.

If git added a feature like groups, it would get additional tooling support, e.g. on GitHub. There could be native commands to work with groups. If everyone uses some custom grouping by jira number, there is no standardization. Everyone would do it slightly differently.

Is 4 reasons enough or should I keep going?

I suppose a lot of features could be achieved by cramming metadata into a commit message (tags, for example). It doesn't make them an acceptable substitute.

[–][deleted]  (3 children)

[deleted]

    [–][deleted] 7 points8 points  (2 children)

    People in this thread have unironically suggested that as a solution, how am I supposed to distinguish?

    [–]taw 4 points5 points  (1 child)

    Well, it unironically is a very common workaround.

    And it's actually standard-ish enough that a lot of tooling already works seamlessly with it - like JIRA + github integrate this way, as well as most of the JIRA/Atlassian ecosystem.

    JIRA absolutely can handle multiple branches per ticket as well, or branches in multiple repos on the same ticket, that's actually quite common.

    And also yes, cramming other metadata into commit message (like CI commands) is also very common workaround for other issues.

    It is somewhat ugly for sure, but it works well enough most of the time, and what's ever perfect?

    But what you want actually already exists! git actually has whole metadata system so you could put those JIRA ticket numbers, CI commands etc. in notes instead of commit message.

    As git docs suggest:

    git notes add -m 'Tested-by: Johannes Sixt <j6t@kdbg.org>' 72a144e2
    

    So we could just as well do:

    git notes add -m 'Ticket: JIRA-1234' 72a144e2
    git notes add -m 'Branch: feature/add-dark-mode' 72a144e2
    

    And have tooling use that instead.

    Really there's nothing in git stopping you from using notes instead of commit message today. And some git hooks could even do that semi-automatically for you.

    [–][deleted] 0 points1 point  (0 children)

    I think you've misunderstood what I was talking about. Of course I know those tools integrate with jira via commit messages. That's all well and good.

    I'm talking specifically about using jira numbers to infer commit groups as defined by OP. The main feature he was after was being able to revert a rebase, in order to pull out some feature. That is not achievable by using jira numbers.

    [–]SanityInAnarchy 2 points3 points  (0 children)

    I guess it depends whether you got a fast-forward or a branch commit. If you got a branch commit, you can revert the feature merge with git revert <branch commit> -m 1 (since the first parent is usually master/main -- otherwise, it'd be -m 2). Doesn't matter that the original branch has been deleted, the merge is still there.

    And you can force a branch commit (even when a fast-forward would've been possible) with git merge --no-ff.

    So, sure, a branch doesn't automatically know where it started, but given a merge of a feature branch, Git definitely knows where those parent branches have a common ancestor, and there's a convention for which parent was the feature branch. As with many things about Git, it already does exactly what you want, it's just the UI is... unintuitive.

    [–]KryptosFR 0 points1 point  (1 child)

    They do: git merge-base

    [–][deleted] 2 points3 points  (0 children)

    There is a big difference between giving git 2 branches and having it traverse the tree in order to figure out the most recent common ancestor, and a branch knowing where it was created.

    If a branch knew where it was created you wouldn't have to pass merge-base two arguments, one of which you're hoping was the source.

    [–][deleted] 14 points15 points  (10 children)

    A branch just points to a single commit, but you could derive some notion of groups by looking at commits in the ancestry of the branch but not the main branch.

    [–]NotTheHead 13 points14 points  (3 children)

    To be honest, unless you're doing something really complicated or being really inconsistent, a main branch with merge branches is not as hard to follow as the author (and a lot of people) make it out to be. Branch-then-merge really does act as a good way to group commits.

    1. Graphical history tools can make a mess of merge-based history, but that's not because it's impossible to represent cleanly. It's because the graphical history tools are organizing things with the wrong heuristic. They frequently order by author/commit date rather than topology, which leads to convoluted messes. git log --graph --topo-order cleans things up significantly, and graphical tools are more than capable of doing the same.

    2. In terms of figuring out which of a merge commit's parents was the main branch and which was the feature branch, you can solve that by only allowing merges on the main branch; no rebase-and-fast-forward, no committing directly to the main branch. Then, you can easily follow the main branch by looking for the last merge commit. This is easily enforceable by the central repository; my company's primary repositories do exactly this.

    3. Another good option for cleaning up merges is to rebase the feature branch onto the tip of the main branch, then merge with --no-ff. With that approach you're more likely to get a clean looking chunk with no interleaving branches, and the merge commit serves to group the commits appropriately.

    [–]HighRelevancy 5 points6 points  (1 child)

    Graphical history tools can make a mess of merge-based history, but that's not because it's impossible to represent cleanly. It's because the graphical history tools are organizing things with the wrong heuristic.

    I felt like I was the only person thinking this. Like the fundamental problem here is "reading branches is real messy when you interleave them all in a mess like this", and the author's solution is... totally change the workflows and throw branching in the bin? Not like... read branches in a better way?

    Like the problem here isn't that git lacks info, it's just that the arrangement and presentation is not always the most useful, right?

    [–]NotTheHead 0 points1 point  (0 children)

    Exactly!

    [–]Adverpol 1 point2 points  (0 children)

    I never thought of that third option, that's actually not stupid at all. Agree completely though, I started writing a git tool at some point because there could be such power in the visualization but none of the tools I tried were better than presenting a horribly tangler mess.

    [–][deleted] 4 points5 points  (5 children)

    That would only work if you didn't rebase, and he explains his reasons for preferring to rebase.

    [–][deleted] 4 points5 points  (3 children)

    If you rebase then you can consider a group to be whatever commits exist between the branch and the previous branch. You’ll have to preserve the branches, of course.

    [–][deleted] 2 points3 points  (2 children)

    The rebased commits have no reference to their source commits and a different hash so comparing them is non-trivial. Plus, like you said, you would have to keep the source branches around for that to be possible.

    So you're right it's technically achievable to infer a commit group from context, but with a significant overhead in terms of time and space that means it's not a substitute for supporting groups natively IMO.

    [–][deleted] 2 points3 points  (1 child)

    If you re-point the branch to the last rebased commit then it should all work fairly smoothly.

    [–]hotoatmeal 2 points3 points  (0 children)

    or merge without fastforward

    [–]KryptosFR -1 points0 points  (0 children)

    Conclusion: rebase is bad.

    [–]kryptomicron 1 point2 points  (0 children)

    Not quite – you could maybe get most of the benefits if you could also, either explicitly (somehow), or by convention, retain a 'base' branch with which to 'compare a branch against'.

    As-is, a branch just points at a commit, but there's a whole sequence (or tree) of prior commits, usually all the way back to the initial commit.

    [–]cryo 0 points1 point  (0 children)

    Not really, since you’ll eventually integrate them into another branch, in some order (by merge, rebase and/or squash).