-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Add a checkout example #4692
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a checkout example #4692
Conversation
Note that while working with refishs, I've grown to like And looking around its API, it might be worthwhile to add a |
There was a problem hiding this 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.
examples/checkout.c
Outdated
# define PRIdZ "zd" | ||
#endif | ||
|
||
/** The following example demonstrates how to do checkouts with libgit2. |
There was a problem hiding this comment.
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
examples/checkout.c
Outdated
/** The following example demonstrates how to do checkouts with libgit2. | ||
* | ||
* Recognized options are : | ||
* --force: don't actually commit the merge. |
There was a problem hiding this comment.
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?
examples/checkout.c
Outdated
|
||
static void print_usage(void) | ||
{ | ||
fprintf(stderr, "usage: checkout [--force] <branch>\n"); |
There was a problem hiding this comment.
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
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); |
There was a problem hiding this comment.
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
examples/checkout.c
Outdated
} | ||
|
||
|
||
|
There was a problem hiding this comment.
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
examples/checkout.c
Outdated
|
||
parse_options(&path, &opts, &args); | ||
|
||
git_libgit2_init(); |
There was a problem hiding this comment.
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.
examples/checkout.c
Outdated
fprintf(stderr, "missing argument\n"); | ||
err = -1; | ||
goto cleanup; | ||
} |
There was a problem hiding this comment.
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
examples/checkout.c
Outdated
err = perform_checkout_ref(repo, checkout_target, &opts); | ||
} else { | ||
printf("failed to resolve %s: %s\n", args.argv[args.pos], giterr_last()->message); | ||
} |
There was a problem hiding this comment.
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);
examples/common.c
Outdated
|
||
neg_opt = malloc(strlen(opt) + 3); | ||
strcpy(neg_opt, "--no-"); | ||
strcat(neg_opt, opt + 2); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 !
examples/checkout.c
Outdated
|
||
#ifdef _MSC_VER | ||
#define snprintf sprintf_s | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is unused
b97c7ba
to
ffc3918
Compare
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. |
examples/common.c
Outdated
res = 1; | ||
} | ||
|
||
return res; |
There was a problem hiding this comment.
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
examples/common.h
Outdated
* 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, |
There was a problem hiding this comment.
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.
const char *curr = args->argv[args->pos]; | ||
int bool_arg; | ||
|
||
if (strcmp(curr, "--") == 0) { |
There was a problem hiding this comment.
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
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)); |
There was a problem hiding this comment.
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
examples/checkout.c
Outdated
print_usage(); | ||
|
||
/* Default values */ | ||
opts->progress = 1; |
There was a problem hiding this comment.
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
} | ||
|
||
if (args.pos >= args.argc) { | ||
fprintf(stderr, "unhandled\n"); |
There was a problem hiding this comment.
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
|
||
fprintf(stderr, "unhandled path-based checkout\n"); | ||
err = 1; | ||
goto cleanup; |
There was a problem hiding this comment.
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
/** | ||
* Try to resolve a "refish" argument to a target libgit2 can use | ||
*/ | ||
err = resolve_refish(&checkout_target, repo, args.argv[args.pos]); |
There was a problem hiding this comment.
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
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.
ffc3918
to
4570fd5
Compare
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 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 😉). |
There was a problem hiding this 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.
|
||
/** | ||
* 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, |
There was a problem hiding this comment.
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
4570fd5
to
b24202e
Compare
Fixed the UB. As always, thanks for the review @pks-t ! |
And again, thanks @tiennou! :) |
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.