On Thu, Aug 16, 2018 at 1:13 AM, Thomas Gummerer <t.gummerer@xxxxxxxxx> wrote: > On 08/08, Paul-Sebastian Ungureanu wrote: >> Add stash create to the helper. >> >> Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@xxxxxxxxx> >> --- >> builtin/stash--helper.c | 406 ++++++++++++++++++++++++++++++++++++++++ >> git-stash.sh | 2 +- >> 2 files changed, 407 insertions(+), 1 deletion(-) >> >> diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c >> index 5ff810f8c..a4e57899b 100644 >> --- a/builtin/stash--helper.c >> +++ b/builtin/stash--helper.c >> @@ -21,6 +21,7 @@ static const char * const git_stash_helper_usage[] = { >> N_("git stash--helper branch <branchname> [<stash>]"), >> N_("git stash--helper clear"), >> N_("git stash--helper store [-m|--message <message>] [-q|--quiet] <commit>"), >> + N_("git stash--helper create [<message>]"), >> NULL >> }; >> >> @@ -64,6 +65,11 @@ static const char * const git_stash_helper_store_usage[] = { >> NULL >> }; >> >> +static const char * const git_stash_helper_create_usage[] = { >> + N_("git stash--helper create [<message>]"), >> + NULL >> +}; >> + >> static const char *ref_stash = "refs/stash"; >> static int quiet; >> static struct strbuf stash_index_path = STRBUF_INIT; >> @@ -781,6 +787,404 @@ static int store_stash(int argc, const char **argv, const char *prefix) >> return do_store_stash(argv[0], stash_msg, quiet); >> } >> >> [...] >> >> + >> +static int do_create_stash(int argc, const char **argv, const char *prefix, >> + const char **stash_msg, int include_untracked, >> + int patch_mode, struct stash_info *info) >> +{ >> + int untracked_commit_option = 0; >> + int ret = 0; >> + int subject_len; >> + int flags; >> + const char *head_short_sha1 = NULL; >> + const char *branch_ref = NULL; >> + const char *head_subject = NULL; >> + const char *branch_name = "(no branch)"; >> + struct commit *head_commit = NULL; >> + struct commit_list *parents = NULL; >> + struct strbuf msg = STRBUF_INIT; >> + struct strbuf commit_tree_label = STRBUF_INIT; >> + struct strbuf out = STRBUF_INIT; >> + struct strbuf final_stash_msg = STRBUF_INIT; >> + >> + read_cache_preload(NULL); >> + refresh_cache(REFRESH_QUIET); >> + >> + if (!check_changes(argv, include_untracked, prefix)) { >> + ret = 1; >> + goto done; > > I wonder if we can just 'exit(0)' here, instead of returning. This > whole command is a builtin, and I *think* outside of 'libgit.a' exiting > early is fine. It does mean that we're not free'ing the memory > though, which means a leak checker would probably complain. So > dunno. It would simplify the code a little, but not sure it's worth it. Indeed, there shouldn't be any problem by calling exit(0). >> + } >> + >> + if (get_oid("HEAD", &info->b_commit)) { >> + fprintf_ln(stderr, "You do not have the initial commit yet"); >> + ret = -1; >> + goto done; >> + } else { >> + head_commit = lookup_commit(the_repository, &info->b_commit); >> + } >> + >> + branch_ref = resolve_ref_unsafe("HEAD", 0, NULL, &flags); >> + if (flags & REF_ISSYMREF) >> + branch_name = strrchr(branch_ref, '/') + 1; >> + head_short_sha1 = find_unique_abbrev(&head_commit->object.oid, >> + DEFAULT_ABBREV); >> + subject_len = find_commit_subject(get_commit_buffer(head_commit, NULL), >> + &head_subject); >> + strbuf_addf(&msg, "%s: %s %.*s\n", branch_name, head_short_sha1, >> + subject_len, head_subject); > > I think this can be written in a slightly simpler way: > > head_short_sha1 = find_unique_abbrev(&head_commit->object.oid, > DEFAULT_ABBREV); > strbuf_addf(&msg, "%s: %s", branch_name, head_short_sha1); > pp_commit_easy(CMIT_FMT_ONELINE, head_commit, &msg); > strbuf_addch(&msg, '\n'); > > The other advantage this brings is that it is consistent with other > places where we print/use the subject of a commit (e.g. in 'git reset > --hard'). Thanks for this suggestion. >> + >> + strbuf_addf(&commit_tree_label, "index on %s\n", msg.buf); >> + commit_list_insert(head_commit, &parents); >> + if (write_cache_as_tree(&info->i_tree, 0, NULL) || >> + commit_tree(commit_tree_label.buf, commit_tree_label.len, >> + &info->i_tree, parents, &info->i_commit, NULL, NULL)) { >> + fprintf_ln(stderr, "Cannot save the current index state"); > > Looks like this message is translated in the current 'git stash' > implementation, so it should be here as well. Same for the messages > below. > >> + ret = -1; >> + goto done; >> + } >> + >> + if (include_untracked && get_untracked_files(argv, 1, >> + include_untracked, &out)) { >> + if (save_untracked_files(info, &msg, &out)) { >> + printf_ln("Cannot save the untracked files"); > > Why does this go to stdout, whereas "Cannot save the current index > state" above goes to stderr? In the shell version of git stash these > all go to stderr fwiw. There are a few similar cases, it would > probably be worth going through all the print statements here and see > if they need to be translated, and to which output stream they should > go. This is what I am going to do. >> + ret = -1; >> + goto done; >> + } >> + untracked_commit_option = 1; >> + } >> + if (patch_mode) { >> + ret = stash_patch(info, argv); >> + if (ret < 0) { >> + printf_ln("Cannot save the current worktree state"); >> + goto done; >> + } else if (ret > 0) { >> + goto done; >> + } >> + } else { >> + if (stash_working_tree(info, argv)) { >> + printf_ln("Cannot save the current worktree state"); >> + ret = -1; >> + goto done; >> + } >> + } >> + >> + if (!*stash_msg || !strlen(*stash_msg)) >> + strbuf_addf(&final_stash_msg, "WIP on %s", msg.buf); >> + else >> + strbuf_addf(&final_stash_msg, "On %s: %s\n", branch_name, >> + *stash_msg); >> + *stash_msg = strbuf_detach(&final_stash_msg, NULL); > > strbuf_detach means we're taking ownership of the memory, so we'll > have to free it afterwards. Looking at this we may not even want to > re-use the 'stash_msg' variable here, but instead introduce another > variable for it, so it doesn't have a dual meaning in this function. > >> + >> + /* >> + * `parents` will be empty after calling `commit_tree()`, so there is >> + * no need to call `free_commit_list()` >> + */ >> + parents = NULL; >> + if (untracked_commit_option) >> + commit_list_insert(lookup_commit(the_repository, &info->u_commit), &parents); >> + commit_list_insert(lookup_commit(the_repository, &info->i_commit), &parents); >> + commit_list_insert(head_commit, &parents); >> + >> + if (commit_tree(*stash_msg, strlen(*stash_msg), &info->w_tree, >> + parents, &info->w_commit, NULL, NULL)) { >> + printf_ln("Cannot record working tree state"); >> + ret = -1; >> + goto done; >> + } >> + >> +done: >> + strbuf_release(&commit_tree_label); >> + strbuf_release(&msg); >> + strbuf_release(&out); >> + strbuf_release(&final_stash_msg); >> + return ret; >> +} >> + >> +static int create_stash(int argc, const char **argv, const char *prefix) >> +{ >> + int include_untracked = 0; >> + int ret = 0; >> + const char *stash_msg = NULL; >> + struct stash_info info; >> + struct option options[] = { >> + OPT_BOOL('u', "include-untracked", &include_untracked, >> + N_("include untracked files in stash")), >> + OPT_STRING('m', "message", &stash_msg, N_("message"), >> + N_("stash message")), >> + OPT_END() >> + }; >> + >> + argc = parse_options(argc, argv, prefix, options, >> + git_stash_helper_create_usage, >> + 0); >> + >> + ret = do_create_stash(argc, argv, prefix, &stash_msg, >> + include_untracked, 0, &info); > > stash_msg doesn't have to be passed as a pointer to a pointer here, as > we never need the modified value after this function returns. I think > just passing 'stash_msg' here instead of '&stash_msg' will make > 'do_create_stash' slightly easier to read. That's right, but `do_create_stash()` is also called by `do_push_stash()`, which will need the modified value. >> + >> + if (!ret) >> + printf_ln("%s", oid_to_hex(&info.w_commit)); >> + >> + /* >> + * ret can be 1 if there were no changes. In this case, we should >> + * not error out. >> + */ >> + return ret < 0; >> +} >> + >> int cmd_stash__helper(int argc, const char **argv, const char *prefix) >> { >> pid_t pid = getpid(); >> @@ -817,6 +1221,8 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix) >> return !!show_stash(argc, argv, prefix); >> else if (!strcmp(argv[0], "store")) >> return !!store_stash(argc, argv, prefix); >> + else if (!strcmp(argv[0], "create")) >> + return !!create_stash(argc, argv, prefix); >> >> usage_msg_opt(xstrfmt(_("unknown subcommand: %s"), argv[0]), >> git_stash_helper_usage, options); >> diff --git a/git-stash.sh b/git-stash.sh >> index 5739c5152..ab06e4ffb 100755 >> --- a/git-stash.sh >> +++ b/git-stash.sh >> @@ -425,7 +425,7 @@ clear) >> ;; >> create) >> shift >> - create_stash -m "$*" && echo "$w_commit" >> + git stash--helper create --message "$*" >> ;; >> store) >> shift >> -- >> 2.18.0.573.g56500d98f >> Thanks!