How do I produce every possible git-status?

Background:

We are currently using git for source management in a web app I am working on. There is an editor, and so there is also a web interface to git.

  • how to import Git project to TFS Cloud
  • Keep a different configuration file (untracked) for each branch
  • git branch: gh-pages
  • Deploying Fb app on Heroku
  • Cloning specific branch
  • Does GIT supports mirroring or proxy concept?
  • One of our use cases is that people can ALSO manage their git repositories from the command line, so the web interface needs to be able to handle, in some way, any weird state it finds the repository in.

    Question:

    For testing, it would be great to get a git repository with a file in every possible state, so I could verify that all possible conditions are handled. Reading “man git-status(1)” I counted possible 24 states (not counting ignored) that a file might be in.

    I have only figured out how to create 17 of these states.

    Here are the XY codes (see git-status), of the states I do not know how to reproduce.

    D           M    deleted from index
    C        [ MD]   copied in index
    D           D    unmerged, both deleted
    A           U    unmerged, added by us
    U           A    unmerged, added by them
    

    There is a gist on github with a ruby script which creates every state I already know how to reproduce, I would love to make that complete.

  • git pull without remotely compressing objects
  • How do I prompt the user from within a commit-msg hook?
  • Why are there two different lines with core.autocrlf output with “git config -l”?
  • How to create .gitignore file
  • Git: Commit to multiple branches at the same time
  • GitHub for Windows says that there are uncommited changes even though the diffs are empty
  • 2 Solutions collect form web for “How do I produce every possible git-status?”

    For what it’s worth, based on the source, there’s no way to get “copied in index” to occur today:

    wt-status.c:            status = d->index_status;
    wt-status.c:            if (!d->index_status)
    wt-status.c:                    d->index_status = p->status;
    wt-status.c:                    d->index_status = DIFF_STATUS_UNMERGED;
    wt-status.c:                    d->index_status = DIFF_STATUS_ADDED;
    wt-status.c:            if (!d->index_status ||
    wt-status.c:                d->index_status == DIFF_STATUS_UNMERGED)
    wt-status.c:    if (d->index_status)
    wt-status.c:            color_fprintf(s->fp, color(WT_STATUS_UPDATED, s), "%c", 
    wt-status.h:    int index_status;
    

    (where index_status is the letter printed for the first column). So the direct assignments can set it to U and A, and the copy-assignment from p->status can set it to whatever p->status is set to. That is ultimately controlled via this bit of code:

    static void wt_status_collect_changes_index(struct wt_status *s)
    {
            struct rev_info rev;
            struct setup_revision_opt opt;
    
            init_revisions(&rev, NULL);
            memset(&opt, 0, sizeof(opt));
            opt.def = s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference;
            setup_revisions(0, NULL, &rev, &opt);
    
            if (s->ignore_submodule_arg) {
                    DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG);
                    handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg);
            }
    
            rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
            rev.diffopt.format_callback = wt_status_collect_updated_cb;
            rev.diffopt.format_callback_data = s;
            rev.diffopt.detect_rename = 1;
            rev.diffopt.rename_limit = 200;
            rev.diffopt.break_opt = 0;
            copy_pathspec(&rev.prune_data, &s->pathspec);
            run_diff_index(&rev, 1);
    }
    

    The diff options here are those shown above: detect_rename is set to DIFF_DETECT_RENAME (1 — this should use the #define, really), with a limit of 200. If detect_rename had been set to DIFF_DETECT_COPY (2), you could get state C.

    I tested this by modifying wt-status.c (see below), then fussing about with another file:

    $ git status --short
     M wt-status.c
    $ git mv zlib.c zzy.c; cp zzy.c zzz.c; git add zzz.c; git status --short
     M wt-status.c
    R  zlib.c -> zzy.c
    A  zzz.c
    $ ./git-status --short
     M wt-status.c
    C  zlib.c -> zzy.c
    R  zlib.c -> zzz.c
    

    note that the equivalent of --find-copies-harder is still not set, so you have to have at least one rename already to get the copied status:

    $ git mv zzy.c zlib.c; ./git-status --short
     M wt-status.c
    A  zzz.c
    

    To get that I had to also add another DIFF_OPT_SET:

    $ git diff
    diff --git a/wt-status.c b/wt-status.c
    index 4e55810..06310e3 100644
    --- a/wt-status.c
    +++ b/wt-status.c
    @@ -494,7 +494,8 @@ static void wt_status_collect_changes_index(struct wt_status
            rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
            rev.diffopt.format_callback = wt_status_collect_updated_cb;
            rev.diffopt.format_callback_data = s;
    -       rev.diffopt.detect_rename = 1;
    +       rev.diffopt.detect_rename = DIFF_DETECT_COPY;
    +       DIFF_OPT_SET(&rev.diffopt, FIND_COPIES_HARDER);
            rev.diffopt.rename_limit = 200;
            rev.diffopt.break_opt = 0;
            copy_pathspec(&rev.prune_data, &s->pathspec);
    $ ./git-status --short
     M wt-status.c
    C  zlib.c -> zzz.c
    

    I’ll stop at this point though, that’s enough playing for now 🙂

    Chris

    By posting this question to the git mailing list, I got an answer for four of the seven codes, with the investigation done by @torek there is now a complete answer to this question, though I don’t quite know how to reflect that in stackoverflow.

    You can read the git mailing list discussion for details, but the short answer is, with the information provided by @torek, none of the status combinations which I cannot reproduce are reachable by normal users of the git command line tools.

    Git Baby is a git and github fan, let's start git clone.