#48 ✓resolved
Alexander Kellett

Staging of single lines

Reported by Alexander Kellett | December 17th, 2008 @ 09:08 PM | in 0.7

Currently its not possible to stage a single line, but only to do a hunk wise stage.

Comments and changes to this ticket

  • Pieter de Bie

    Pieter de Bie December 17th, 2008 @ 09:53 PM

    • Tag set to commit, feature
    • State changed from “new” to “open”

    I'd like to do this too. I once started working on it, but I stopped because I couldn't find a nice way to integrate it in the GUI. I'd rather not have to use context menu's to do this.

    Do you have any suggestions?

  • elliottcable

    elliottcable December 27th, 2008 @ 11:31 PM

    • Tag changed from commit, feature to commit, feature, gui

    I'd just like to point out an alternative, from a ticket that was marked as duplicate.

    The normal git interface doesn't allow staging of single lines, just hunks, and then allows 'splitting' of hunks.

    So what would be really nice, is two seperate settings - the first being a selection menu/textbox to set the 'hunk size' (i.e. number of required lines of padding on either side to constitute a 'seperate hunk' - the git defaults in git add --interactive seem to be 4 by default, and then 2 if you use the split command when staging), the second being the ability to click a line number to stage only that line.

    On that note, I think a much better UI would be one file list, of all files changed in the working directory AND the stage. Files should be three-way diffs between unstaged/staged/last commit. Red lines are unstaged, green lines are staged, blue lines are last commit.

    I could mockup if you really want.

  • Pieter de Bie

    Pieter de Bie December 31st, 2008 @ 04:43 PM

    Sorry for being a bit unresponsive, but I'm a bit busy with moving to another country and the holidays :)

    Changing the hunk size is already one of the things I want to do. In fact, there's a branch called 'pb/commit_context' or so, which does exactly that. It adds a slider to the commit view which you can adjust to change hunk sizes. You can check it out if you want and see if that is what you mean.

    Combining the staged / unstaged lists into one big diff is something I really don't like. It'll make it very hard to see what you're going to commit now (and which files you'll change). Having the clean separation between staged / unstaged is something that I personally use all the time.

    That said, I think there's room in GitX for a view that simplifies the staged / unstaged part. There have been more requests for that and I understand that the distinction can be a bit difficult to understand for git beginners or for non-programmers.

    One way to resolve this is to have a view which does not use the staged / unstaged bit, but only allows you to commit whole files. That would greatly simplify the system, and still be of use with some files, especially with binary changes (think of Photoshop files or images).

    I'm not sure if having a view which has one diff for the unstaged / staged changes is useful. I'm afraid that it will only cause confusion, for example when doing a change, adding that to the index, and then reverting the change. The three-way diff will be messy and difficult to comprehend.

    Having said that, I'm of course always open to suggestions. If you can show me a nice and (more) intuitive interface, I want to see it :)

  • elliottcable

    elliottcable December 31st, 2008 @ 11:55 PM

    Okay, this mockup is absolutely horrible. But I hope it'll get some of my ideas across.


    I envision a much more mac-y, sexy interface for the same general idea.

  • Christopher Bowns

    Christopher Bowns January 17th, 2009 @ 02:54 AM

    Is the ticket for this? All I want is a "split" option on each hunk in the staging view.

    The UI seems fairly obvious: http://skitch.com/cbowns/bbqdd/b...

    Just add a "Split" button right next to "Stage". Both actions operate on a per-hunk basis, so as a user, that's the place I'd look for it.

  • elliottcable

    elliottcable January 17th, 2009 @ 04:12 AM

    Eww @ split. That makes GitX no better than git add --interactive - the goal is to be better, which means adding true per-line hunking functionality.

  • Christopher Bowns

    Christopher Bowns January 18th, 2009 @ 08:33 AM

    Eww @ split. That makes GitX no better than git add --interactive - the goal is to be better, which means adding true per-line hunking functionality.

    Disagree: putting a nice visual, colored diff GUI on top of split/stage is exactly what GitX should be about in the staging view.

  • elliottcable

    elliottcable January 18th, 2009 @ 09:27 AM

    Well... I think that's just you. Or maybe this is just me.

    But personally, and I had assumed people were like me, and were excited about GitX due to the ability to have more fine-grained control on what's going on, in real time, while they edit their files.

    Again, maybe that's just me, and the rest of the possible userbase is like you. But I know that GitX just being a 'prettier' interface to the command line tools leaves me mostly uninterested in it.

  • Pieter de Bie

    Pieter de Bie January 18th, 2009 @ 09:08 PM

    I agree with elliot; the split thing in 'git add --interactive' is OK for a CLI program, but with a GUI you should have something more intuitive. Problem with the split thing is that you're not sure where the split will be. If you need a split of the hunk, chances are a single one won't suffice, but you'll have to do multiple splits.

    I'd much rather see some kind of GUI where you can select the lines you want to stage.

  • Alexander Kellett

    Alexander Kellett January 18th, 2009 @ 09:31 PM

    I like the idea of splitting. But making use of the mouse to allow precision manual splits. The other options I've thought about are either difficult to learn about without reading about (I'm not sure I / you consider this an issue, but it may be), or, simply ugly.

    This idea of having the ability to split hunks at points with the mouse seems intuitive, discoverable and could possibly even be pretty.

    I like it. Assuming thats what the poster meant :D

  • elliottcable

    elliottcable January 18th, 2009 @ 10:20 PM

    I've got a somewhat better idea, possibly. What about a dragging sort of thing? Drag from one line to another, that group of lines becomes a "hunk". Then you can stage, the hunk, unstage it later, do anything else you'd normally do with a hunk. Changes to the file would become a part of that 'hunk' if they were between the first and last lines of the hunk you selected. You can also 'disband' a hunk, removing your setting of it from first to last lines.

    The idea is that you won't often change what a hunk is to you, you see? If I decide I'm going to change method X in my file, I drag from the method definition's start to it's end, and I won't often want to change that before I commit, even if I make more changes to the file in that method.

  • Pieter de Bie

    Pieter de Bie January 18th, 2009 @ 10:24 PM

    Yes, the drag and drop is exactly what I meant by my previous post.

  • Pieter de Bie

    Pieter de Bie January 18th, 2009 @ 10:25 PM

    Having said that, I'm not entirely sure how that would work in Javascript. If anyone has any experience with that, and would be able to come up with a nice mockup, that would be very welcome :)

  • Alexander Kellett

    Alexander Kellett January 18th, 2009 @ 10:31 PM

    Ah... interesting. The drag and drop / selection idea is pretty much my "difficult to learn about without reading about" idea.

    Maybe just reuse text selection, then the JS side of things becomes pretty trivial. A "(un)stage lines" could appear above the hunk after making a text selection. I worry about the fact that the text selection is not per line... but maybe in practice it works well.

  • Pieter de Bie

    Pieter de Bie January 25th, 2009 @ 04:43 PM

    • Milestone set to 0.7

    I wasn't so much suggestion the "drop" part, but rather, selecting some lines by dragging. If you're done with that, the selected part contains some kind of border and a button with "stage this selection" or something.

    That should be pretty intuitive, right?

    Anyway, I'm putting this on the list for 0.7

  • elliottcable

    elliottcable January 26th, 2009 @ 03:18 AM

    That sounds sexy to me, Pieter!

  • Alexander Kellett

    Alexander Kellett January 26th, 2009 @ 10:41 AM

    Agreed! Sounds great! Thanks for pushing for 0.7!

  • Dave Grijalva

    Dave Grijalva January 26th, 2009 @ 09:49 PM

    ++ using selection.

    If text is selected, just add a "(un)stage selection" button next to the (un)stage button.

  • Pieter de Bie

    Pieter de Bie May 29th, 2009 @ 12:25 AM

    I've uploaded a little example of what I thought would be nice here:


    I've put the source of the example here: http://frim.frim.nl/dragtest.html

    you need to put it in GitX's html/ directory and then run it.

    This isn't complete, as you still need to hook up the logic for figuring out which part is selected, and adjusting that to create a valid diff. Then you need to run that (easy), and adjust the diff view without the selected part, or just do it the easy way and reload the diff.

    I'm hoping someone else will pick this up and finish it :)

  • Johannes Gilger

    Johannes Gilger June 1st, 2009 @ 06:36 PM

    I had a somewhat longer look at it and currently see two major problems:

    1. When selecting part of a hunk we have to read the range-information, and then compute the offset of the selection from the range-information-line to construct a new range-information. Also we should supply a correct number of lines, and not just the first line in the range. Using --recount is no valid option for ambiguous lines.

    2. Right now it's possible to select across hunks, which can't simply be supplied to git apply. One would have to filter the range-informations a (internally) create multiple hunks to supply to git apply.

  • Pieter de Bie

    Pieter de Bie June 1st, 2009 @ 09:24 PM

    Yes, you'll need to recalculate the hunk headers, but that shouldn't be too hard. Just take the original header, and change it depending on whether you add lines or not.

    I don't see why you need more hunks that you'd originally have? Why can't you apply multiple hunks? As long as your selection is continious, it shouldn't be a problem.

  • JDS

    JDS June 9th, 2009 @ 09:05 PM

    I've spent some time improving Pieter's dragtest.html, so that it:

    1) works correctly on all files (closure in loop problem).
    2) allows double-clicking red/green lines to select the whole subhunk (the same portion git-add --interactive would split out).
    3) Only shows the "Stage lines" button if an add/del line is selected (context only "diffs" are no diffs at all).
    4) Constrains selections to a single hunk. There are arguments each way on this one, but from my point of view, there are already tools for applying multiple hunks at once (e.g. double click an entire file). Keeping line-at-a-time at the sub-hunk level is more intuitive, IMO.
    5) Don't lose button release events.

    You can get it here:


    I included a much larger diff to test speed. One problem I know about currently: for large hunks, double-clicking a large subhunk, or dragging out a large range, puts the "Stage lines" button off screen.

    Save source into your gitx/html directory, and 'open dragtest.html'.

    Comments welcome. Once the UI is nailed down, we can implement the back end.

  • Johannes Gilger

    Johannes Gilger June 9th, 2009 @ 09:27 PM

    Wow, very nice work. Good to have someone with JavaScript skills on board ;)

    A few comments/opinions:

    1. Nice
    2. Hmm, doesn't always select the whole hunk for me, if there are context-lines in between additions/removals. But maybe I just didn't understand your description, and this is the wanted behavior (which would be fine!)
    3. Yep, good to have that.
    4. That's one of the points we could argue about. Do we really need the selection to work across hunks (which would probably make the whole issue more complex)? For me the behavior right now is cool enough.

    An issue is that the files/line-numbers don't disappear if a hunk is staged, but this might go away once we hook it up to the backend logic. I'm not that familiar with the code at this point.

    Do you intend to stay with this issue? This would help us and bring us a lot closer to 0.7.

  • JDS

    JDS June 9th, 2009 @ 11:03 PM

    Well, don't get too excited, my javascript skills consist of typing "javascript blah blah" into Google over and over, and making sweeping generalizations from other languages I know ;).

    Double click is supposed to select the whole "sub-hunk", i.e. a group of contiguous add/del lines, and their surrounding context. Let me know if it's not. Think of it as "this block of changes, who cares what diff thinks". This is the granularity git-add --interactive uses, btw. Since there will already be a "[Stage]" button for each full hunk, there's no need to have another method to select it in entirety.

    This file is just a cooked-up test for the interface from Pieter. Once the backend is hooked up, sub-hunks or individual lines will be staged, and then trigger a reload, getting line numbers correct. I'm not sure what happens now if you stage a hunk in the middle of long diff list with a scroll bar (anyone care to try and report... does it lose your place?).

    I can put together the backend stuff if we reach agreement on the interface. A couple weeks back, I actually coded up a full "[Split]" button to go alongside "[Stage]" button. I'm using it now, and it's like git-add --interactive except not horribly painful. In the meantime, however, Pieter and others moved on to this selecting lines paradigm, stranding my "[Split]" work. So if I do go on to the back-end, I'd want some positive indications that it wouldn't go that way again!

    P.S. Slight fix (when starting selection on an existing selection) loaded at the http://idlwave.org/download/dragtest.html. Also, normal text selection in the page is now disabled (confusingly the same color)... I wonder if people will be bothered not being able to select text in this view?

  • Johannes Gilger

    Johannes Gilger June 9th, 2009 @ 11:17 PM

    Ok, we have the same understanding of the double-click stuff.

    I'm sorry for the lost work on your Split feature. Did you announce it here somewhere? Did you mail it to Pieter? Did you push it to Github and rebase it on master from time to time? I usually check the GitHub network of GitX a few times a day to see hot new features in progress.

    Don't worry about this going away. It's queued for 0.7 and is probably the biggest and coolest feature.

    Let's wait and see what Pieter has to say about the interface and about the backend-stuff.

  • JDS

    JDS June 9th, 2009 @ 11:30 PM


    I had emailed the patch set. Not the end of the world, but just don't want to repeat. I do need to get a GitHub acct.

  • JDS

    JDS June 10th, 2009 @ 09:11 PM

    OK, I put my changes up on GitHub and sent a pull req. Nothing yet beyond the interface test.

  • Johannes Gilger

    Johannes Gilger June 10th, 2009 @ 10:38 PM

    I hope you didn't just send the pull-request to me. I'm not the maintainer ;)

    Anyway, as far as I'm concerned I like the interface. If I can read the code right the only thing missing now would be a method which takes the range and computes a new hunk-header which can be passed to "git apply" along with the + and - lines, right?

  • Johannes Gilger

    Johannes Gilger June 10th, 2009 @ 10:40 PM

    Hm, on second thought, this could be done by the Objective-C backend which gets passed: filename, start-line, end-line, +--lines. (I'm happy to avoid as much JavaScript as possible).

  • JDS

    JDS June 10th, 2009 @ 11:52 PM

    Sent to you and Pieter. I planned to recompute the hunk fragments in javascript (maybe by adding optional parameters to getHunkText), and pass to Controller.stageHunk_reverse_, just as addHunk does (which I presume calls git-apply). If others would rather I pass meta-info on the subhunk into ObjC to compute the hunk offsets and pass on to git-apply, I'm willing to do this, but can't myself code that routine. It was a fairly straightforward calculation, so I think JS can handle it fine.

  • Pieter de Bie

    Pieter de Bie June 11th, 2009 @ 12:06 AM


    I haven't had time to look at your patch yet (it's quite large :)),
    but the 5 points you mentioned earlier seem OK. I originally wanted to
    be able to select more than 1 hunk, so that you could also use
    copy-paste (should be easy to implement), but that can wait.

    Computing the hunk in JS is fine for now. In the future we might want
    to offload all hunk stuff to Obj-C (including staging / reverting
    normal hunks) by just passing on line numbers, but that can also wait.

    Thanks for working on this!

  • JDS

    JDS June 16th, 2009 @ 06:00 PM

    OK, it's done! I sent Pieter the pull request. Others can try my work at:

    I adapted Pieter's concept UI as follows:

    1. Line ranges can only be selected within single hunks.
    2. Double-clicking on a colored line selects the contiguous block of additions/deletions.
    3. Shift clicking extends or truncates the existing selection (subject to #1).

    The "Stage Lines" (or "Unstage Lines") button appears at right on the first line of the selection, but only if the selection contains something aside from context. Some effort went into pre-computing the selection range and skipping it if unchanged, which required carefully walking the DOM tree accounting for the #selected div and its children. This is a bit of a fragile setup: if for instance the "Stage Lines" button were moved somewhere else, the DOM walking code would need modification.

    Computing the sub-hunk from the selected line(s) turned out to be not so trivial as you might expect. You can't just take the lines, offset the start line number and count, and pass it to git -apply. Instead, you must for example alter unselected removals to serve as context for the stage (they're not being removed, after all). I looked to git gui citool's TCL code for some of this, but have omitted one "refinement" they mention, which I haven't fully understood (from diff.tcl):

            # There is a special situation to take care of. Consider this hunk:
            #    @@ -10,4 +10,4 @@
            #     context before
            #    -old 1
            #    -old 2
            #    +new 1
            #    +new 2
            #     context after
            # We used to keep the context lines in the order they appear in the
            # hunk. But then it is not possible to correctly stage only
            # "-old 1" and "+new 1" - it would result in this staged text:
            #    context before
            #    old 2
            #    new 1
            #    context after
            # (By symmetry it is not possible to *un*stage "old 2" and "new 2".)
            # We resolve the problem by introducing an asymmetry, namely, when
            # a "+" line is *staged*, it is moved in front of the context lines
            # that are generated from the "-" lines that are immediately before
            # the "+" block. That is, we construct this patch:
            #    @@ -10,4 +10,5 @@
            #     context before
            #    +new 1
            #     old 1
            #     old 2
            #     context after
            # But we do *not* treat "-" lines that are *un*staged in a special
            # way.
            # With this asymmetry it is possible to stage the change
            # "old 1" -> "new 1" directly, and to stage the change
            # "old 2" -> "new 2" by first staging the entire hunk and
            # then unstaging the change "old 1" -> "new 1".
            # This is non-empty if and only if we are _staging_ changes;
            # then it accumulates the consecutive "-" lines (after converting
            # them to context lines) in order to be moved after the "+" change
            # line.

    Input welcome on this issue. Also, testing single/multiple selection (un)staging on various cases is probably a good idea.


    1. Dragging off the screen scrolls.
    2. Fix mouse focus issues on staging: minimize times with "nothing selected".
    3. Evaluate whether the "Stage Lines" button might be better in a fixed location (for large selection ranges).
    4. Refactor single line selection code for use in diff history.
  • Pieter de Bie

    Pieter de Bie June 16th, 2009 @ 06:20 PM

    This is just a comment on the stage-line thing from the TCL. It's worded a bit weirdly, but what they mean is that 'new1' and 'old1' are similar lines, but with a minor difference. Here is a IRL example. Let's say we have this change, in this screenshot:


    if we then stage only the 'NSRect ownRect;' => 'NSRect own_Rect;' part, we get this in the staged view:


    Now, the NSRect is declared after it's used, so this would give a syntax error in the C code. You can avoid it by putting the added lines before the context-lines that would otherwise be deleted lines, like the example in the TCL comment shows.

    So, instead of generating this patch:

    @@ -210,8 +210,8 @@ - (void) drawWithFrame: (NSRect) rect inView:(NSView *)view
            if (cellInfo && ![controller hasNonlinearPath]) {
                    float pathWidth = 10 + 10 * cellInfo.numColumns;
    -               NSRect ownRect;
                    NSDivideRect(rect, &ownRect, &rect, pathWidth, NSMinXEdge);
    +               NSRect own_Rect;
                    int i;
                    struct PBGitGraphLine *lines = cellInfo.lines;

    We should generate something like

    @@ -210,8 +210,8 @@ - (void) drawWithFrame: (NSRect) rect inView:(NSView *)view
            if (cellInfo && ![controller hasNonlinearPath]) {
                    float pathWidth = 10 + 10 * cellInfo.numColumns;
    +               NSRect own_Rect; 
    -               NSRect ownRect;
                    NSDivideRect(rect, &ownRect, &rect, pathWidth, NSMinXEdge);
                    int i;
                    struct PBGitGraphLine *lines = cellInfo.lines;

    I guess it's just a coincidence by the way git produces patches that this works

  • Pieter de Bie

    Pieter de Bie June 16th, 2009 @ 06:35 PM

    I'm sorry, that last example wasn't really good, because we do two patches rather than one. I think I mean we should do

    @@ -210,8 +210,8 @@ - (void) drawWithFrame: (NSRect) rect inView:(NSView *)view
            if (cellInfo && ![controller hasNonlinearPath]) {
                    float pathWidth = 10 + 10 * cellInfo.numColumns;
    +               NSRect own_Rect; 
                    NSRect ownRect;
                    NSDivideRect(rect, &ownRect, &rect, pathWidth, NSMinXEdge);
                    int i;
                    struct PBGitGraphLine *lines = cellInfo.lines;

    Rather than

    @@ -210,8 +210,8 @@ - (void) drawWithFrame: (NSRect) rect inView:(NSView *)view
            if (cellInfo && ![controller hasNonlinearPath]) {
                    float pathWidth = 10 + 10 * cellInfo.numColumns;
                    NSRect ownRect;
                    NSDivideRect(rect, &ownRect, &rect, pathWidth, NSMinXEdge);
    +               NSRect own_Rect;
                    int i;
                    struct PBGitGraphLine *lines = cellInfo.lines;

    when we stage only the addition

  • JDS

    JDS June 16th, 2009 @ 07:47 PM

    Thanks, somewhat clearer. I can confirm this behavior on a test file. What's not clear is whether there is a simple rule when making multi-line patches which will also work. Move all +lines at the beginning of the selection before all -lines directly preceding? Does it really only apply to single line stages? Experimentation welcome.

  • Pieter de Bie

    Pieter de Bie June 19th, 2009 @ 04:36 PM

    I think that you can do the general heuristic that if you only stage
    added lines, and the lines directly before the added lines are deleted
    lines, you have to put the added lines in first, and then change all
    the deleted lines to context lines that are directly after the added

    There might be other, subtle, variants, but that should cover almost
    everything and we can see what we need to change if someone complains.
    It's not really a bug anyway, more a heuristic that works most of the
    time, as you have two different ways to create the patch anyway.

  • JDS

    JDS June 19th, 2009 @ 05:07 PM

    Hmmm... what about

    - -



    where > indicates selection for staging? Should the + lines be migrated forward in this case?

  • JDS

    JDS June 19th, 2009 @ 05:08 PM

    That didn't come out right:


    > + > + > -

  • JDS

    JDS June 19th, 2009 @ 05:09 PM

    OK, this thing really needs a "preview".

    subtract line subtract line selected add line selected add line selected subtract line

  • Pieter de Bie

    Pieter de Bie June 19th, 2009 @ 05:11 PM

    No, in that case you should do what you do now. In that case the
    additions are 'bound' with the selected removal, so you can't change
    the order.

    This only applies to selections that only consist of additions

  • JDS

    JDS July 8th, 2009 @ 11:46 PM

    I've amended the patches in response to Pieter's suggestions, so this ticket can be closed once applied to his repository. We should put some information regarding usage:

    Click-drag within hunks to select any subset of lines for staging. Must include at least one add/del line.

    To stage them, click the Stage Line(s) button at right.

    Double click on any add/del line to select the contiguous set of changes. This is the granularity of git add --interactive, and can be useful for pulling out large subhunks.

    Shift click to extend the selection in either direction.

  • Johannes Gilger

    Johannes Gilger August 18th, 2009 @ 12:02 PM

    Hey Pieter,

    any further developments on this? This has been lying around for quite some time now, which is a shame, since we're only two tickets away from releasing 0.7. I know I haven't kicked people in the butt in the last two months either ;)

  • Johannes Gilger

    Johannes Gilger September 1st, 2009 @ 09:16 AM

    • State changed from “open” to “resolved”

    Ok, it's in the tree so I think we can close this ticket.

  • Martha Wilson

    Martha Wilson February 18th, 2018 @ 03:01 PM

    The classified ads website Donedeal.ie run a featured charity scheme where every two months they very kindly donate a percentage of profits to a selected ...
    best survival games online

  • milan joy

Please Sign in or create a free account to add a new ticket.

With your very own profile, you can contribute to projects, track your activity, watch tickets, receive and update tickets through your email and much more.

New-ticket Create new ticket

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

GitX is the nice-looking gitk clone for OS X

Referenced by