Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I produce every possible git-status?

Tags:

git

jgit

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.

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.

like image 697
mtoy Avatar asked Apr 01 '14 17:04

mtoy


People also ask

How do I add everything to my git status?

Enter git add --all at the command line prompt in your local project directory to add the files or changes to the repository. Enter git status to see the changes to be committed.

How do I git status?

To check the status, open the git bash, and run the status command on your desired directory.


2 Answers

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.

like image 55
mtoy Avatar answered Oct 29 '22 16:10

mtoy


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

like image 21
torek Avatar answered Oct 29 '22 16:10

torek