Skip to content

Add a checkout example#4692

Merged
pks-t merged 5 commits into
libgit2:masterfrom
tiennou:examples/checkout
Jul 20, 2018
Merged

Add a checkout example#4692
pks-t merged 5 commits into
libgit2:masterfrom
tiennou:examples/checkout

Conversation

@tiennou
Copy link
Copy Markdown
Contributor

@tiennou tiennou commented Jun 23, 2018

This adds an example on how to perform checkouts with libgit2.

Note that it only does the "branch moving" part of checkout, so no git checkout src/file.c.

Ref #2824.

@tiennou
Copy link
Copy Markdown
Contributor Author

tiennou commented Jun 23, 2018

Note that while working with refishs, I've grown to like git_annotated_commit for its ability to abstract away the details (like the automated reflog messages).

And looking around its API, it might be worthwhile to add a git_repository_set_head_from_annotated, that would underlie all others set_heads we have. Also, I was a little confused that all the "virtual commits" creation lies in "merge.c", so maybe moving that back to "annotated.c" so the separation is cleaner ?

Copy link
Copy Markdown
Member

@pks-t pks-t left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this example, @tiennou.

While there's a lot of comments, I guess most of them are fast to address. One thing I'd like to see though is to have a lot more /** */-style documentation. The examples are being parsed and presented on Doxygen to serve as some kind of walkthrough with explanations, so that would benefit users a lot, I guess. It's not a requirement to get this merged, though.

Comment thread examples/checkout.c Outdated
# define PRIdZ "zd"
#endif

/** The following example demonstrates how to do checkouts with libgit2.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: multiline-comments usually start with an empty /** line

Comment thread examples/checkout.c Outdated
/** The following example demonstrates how to do checkouts with libgit2.
*
* Recognized options are :
* --force: don't actually commit the merge.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment looks like a copy-paste error?

Comment thread examples/checkout.c Outdated

static void print_usage(void)
{
fprintf(stderr, "usage: checkout [--force] <branch>\n");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're missing the other options

Comment thread examples/checkout.c Outdated
static void print_checkout_progress(const char *path, size_t completed_steps, size_t total_steps, void *payload)
{
(void)payload;
printf("checkout: %s %" PRIuZ "/%" PRIuZ "\n", path, completed_steps, total_steps);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usually you'd have to first erase the previous line and write on that same line again. But for a simple example this is sufficient, I guess

Comment thread examples/checkout.c Outdated
}



Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few blank lines too much

Comment thread examples/checkout.c Outdated

parse_options(&path, &opts, &args);

git_libgit2_init();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to check the return value of that function.

Comment thread examples/checkout.c Outdated
fprintf(stderr, "missing argument\n");
err = -1;
goto cleanup;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check looks like it belongs to parse_opts, as well

Comment thread examples/checkout.c
err = perform_checkout_ref(repo, checkout_target, &opts);
} else {
printf("failed to resolve %s: %s\n", args.argv[args.pos], giterr_last()->message);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd use our usual error handling style instead.

if (err < 0) {
    printf("failed...");
    goto cleanup;
}

err = perform_checkout_ref(repo, checkout_target, &opts);

Comment thread examples/common.c Outdated

neg_opt = malloc(strlen(opt) + 3);
strcpy(neg_opt, "--no-");
strcat(neg_opt, opt + 2);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems a bit heavy-handed ;)

if (!strncmp(found, "--no-", strlen("--no-")) &&
    !strcmp(found + strlen("--no-"), opt + 2)) {
        *out = 0;
        res = 1;
}

This is untested, but something like that would work.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, that's soo much better !

Comment thread examples/checkout.c Outdated

#ifdef _MSC_VER
#define snprintf sprintf_s
#endif
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unused

@tiennou tiennou force-pushed the examples/checkout branch 2 times, most recently from b97c7ba to ffc3918 Compare June 30, 2018 13:05
@tiennou
Copy link
Copy Markdown
Contributor Author

tiennou commented Jun 30, 2018

I've fixed/clarified most of the review comments. Clarifications because sometime™ we might want to demonstrate path-based checkouts, so the code in main was convoluted because of that.

The code is also commented now.

Comment thread examples/common.c Outdated
res = 1;
}

return res;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could avoid the res variable by just returning 1 in the previous branch and 0 at the end of this function. That would make the asymmetry between the first and second conditional go away. Not urgent though, you should only address this if you're rerolling anyway

Comment thread examples/common.h
* Check current `args` entry against a "bool" `opt` (ie. --[no-]progress).
* If `opt` matches positively, out will be set to 1, or if `opt` matches
* negatively, out will be set to 0, and 1 will be returned.
* If neither the positive or the negative form of opt matched, out will be -1,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation is wrong. You either set out to 0 or 1, but never -1.

Comment thread examples/checkout.c
const char *curr = args->argv[args->pos];
int bool_arg;

if (strcmp(curr, "--") == 0) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should probably use !strcmp, for consistency with the next conditional. Might confuse readers otherwise

Comment thread examples/checkout.c Outdated
if (git_annotated_commit_ref(target)) {
err = git_repository_set_head(repo, git_annotated_commit_ref(target));
} else {
err = git_repository_set_head_detached(repo, git_annotated_commit_id(target));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's git_repository_set_head_detached_from_annotated

Comment thread examples/checkout.c
print_usage();

/* Default values */
opts->progress = 1;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should memset(opts, 0, sizeof(opts)), as you never initialize the struct to all-zeroes

Comment thread examples/checkout.c
}

if (args.pos >= args.argc) {
fprintf(stderr, "unhandled\n");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this check be part of parse_options, together with usage instructions? I'd expect that function to only ever return where the options are correct

Comment thread examples/checkout.c

fprintf(stderr, "unhandled path-based checkout\n");
err = 1;
goto cleanup;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd also just remove this and error out in parse_options in case where paths are given. It makes the remaining logic a bit easier to read, I guess

Comment thread examples/checkout.c
/**
* Try to resolve a "refish" argument to a target libgit2 can use
*/
err = resolve_refish(&checkout_target, repo, args.argv[args.pos]);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolving the ref here makes sense, but I'd parse the reference itself into the options structure in parse_options

tiennou added 4 commits July 7, 2018 13:10
As git_annotated_commit seems to behave like cgit's refish, it's quite
helpful to abstract away "targets" via git_annotated_commit_from_id/from_ref.
As the former is accessible via git_annotated_commit_id, make the latter
also available to users.
@tiennou tiennou force-pushed the examples/checkout branch from ffc3918 to 4570fd5 Compare July 7, 2018 12:52
@tiennou
Copy link
Copy Markdown
Contributor Author

tiennou commented Jul 7, 2018

I've fixed some of the comments.

It seems we have a slight difference in how we expect option parsing to happen though, so here's my "view" : I can't access libgit2 while parsing options, because there might be a --git-dir option after the current argument (option which I'm using to test), so I can't resolve refishs, or paths, because the "current working directory" might be updated later. Hence, I really much prefer option parsing to be kept dumb, and I think it actually makes more sense as example code (if you want to perform a checkout, you can basically look at what main() is doing and ignore the option parsing).

Also, I've attempted to handle path-based checkouts, but just ended up craving for libgit2's already written support for those (it only took one "any directory relative stat call" to throw my hands up at the incoming strcat mess 😉).

Copy link
Copy Markdown
Member

@pks-t pks-t left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems we have a slight difference in how we expect option parsing to happen though, so here's my "view" : I can't access libgit2 while parsing options, because there might be a --git-dir option after the current argument (option which I'm using to test), so I can't resolve refishs, or paths, because the "current working directory" might be updated later. Hence, I really much prefer option parsing to be kept dumb, and I think it actually makes more sense as example code (if you want to perform a checkout, you can basically look at what main() is doing and ignore the option parsing).

Fair enough, so be it.

I'm fine with this PR then, except for one instance of undefined behaviour. Ready to merge as soon as that's fixed.

Comment thread examples/checkout.c

/**
* This function is called to report progression, ie. it's called once with
* a NULL path and the number of total steps, then for each subsequent path,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this function is indeed called with a NULL path, then the printf call is undefined behaviour as "%s" expects a valid pointer

@tiennou tiennou force-pushed the examples/checkout branch from 4570fd5 to b24202e Compare July 17, 2018 19:43
@tiennou
Copy link
Copy Markdown
Contributor Author

tiennou commented Jul 17, 2018

Fixed the UB. As always, thanks for the review @pks-t !

@pks-t pks-t merged commit ea9e2c1 into libgit2:master Jul 20, 2018
@pks-t
Copy link
Copy Markdown
Member

pks-t commented Jul 20, 2018

And again, thanks @tiennou! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants