From: Junio C Hamano Date: Fri, 5 Jun 2015 19:17:36 +0000 (-0700) Subject: Merge branch 'jk/at-push-sha1' X-Git-Tag: v2.5.0-rc0~55 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/c4a8354bc14e20d5ca6dc353e17e5b27fefefdab?ds=inline;hp=-c Merge branch 'jk/at-push-sha1' Introduce @{push} short-hand to denote the remote-tracking branch that tracks the branch at the remote the would be pushed to. * jk/at-push-sha1: for-each-ref: accept "%(push)" format for-each-ref: use skip_prefix instead of starts_with sha1_name: implement @{push} shorthand sha1_name: refactor interpret_upstream_mark sha1_name: refactor upstream_mark remote.c: add branch_get_push remote.c: return upstream name from stat_tracking_info remote.c: untangle error logic in branch_get_upstream remote.c: report specific errors from branch_get_upstream remote.c: introduce branch_get_upstream helper remote.c: hoist read_config into remote_get_1 remote.c: provide per-branch pushremote name remote.c: hoist branch.*.remote lookup out of remote_get_1 remote.c: drop "remote" pointer from "struct branch" remote.c: refactor setup of branch->merge list remote.c: drop default_remote_name variable --- c4a8354bc14e20d5ca6dc353e17e5b27fefefdab diff --combined builtin/branch.c index 9cbab189f5,e4d184d69a..c70085c35f --- a/builtin/branch.c +++ b/builtin/branch.c @@@ -123,14 -123,12 +123,12 @@@ static int branch_merged(int kind, cons if (kind == REF_LOCAL_BRANCH) { struct branch *branch = branch_get(name); + const char *upstream = branch_get_upstream(branch, NULL); unsigned char sha1[20]; - if (branch && - branch->merge && - branch->merge[0] && - branch->merge[0]->dst && + if (upstream && (reference_name = reference_name_to_free = - resolve_refdup(branch->merge[0]->dst, RESOLVE_REF_READING, + resolve_refdup(upstream, RESOLVE_REF_READING, sha1, NULL)) != NULL) reference_rev = lookup_commit_reference(sha1); } @@@ -242,7 -240,7 +240,7 @@@ static int delete_branches(int argc, co sha1, &flags); if (!target) { error(remote_branch - ? _("remote branch '%s' not found.") + ? _("remote-tracking branch '%s' not found.") : _("branch '%s' not found."), bname.buf); ret = 1; continue; @@@ -257,7 -255,7 +255,7 @@@ if (delete_ref(name, sha1, REF_NODEREF)) { error(remote_branch - ? _("Error deleting remote branch '%s'") + ? _("Error deleting remote-tracking branch '%s'") : _("Error deleting branch '%s'"), bname.buf); ret = 1; @@@ -265,7 -263,7 +263,7 @@@ } if (!quiet) { printf(remote_branch - ? _("Deleted remote branch %s (was %s).\n") + ? _("Deleted remote-tracking branch %s (was %s).\n") : _("Deleted branch %s (was %s).\n"), bname.buf, (flags & REF_ISBROKEN) ? "broken" @@@ -427,25 -425,19 +425,19 @@@ static void fill_tracking_info(struct s int ours, theirs; char *ref = NULL; struct branch *branch = branch_get(branch_name); + const char *upstream; struct strbuf fancy = STRBUF_INIT; int upstream_is_gone = 0; int added_decoration = 1; - switch (stat_tracking_info(branch, &ours, &theirs)) { - case 0: - /* no base */ - return; - case -1: - /* with "gone" base */ + if (stat_tracking_info(branch, &ours, &theirs, &upstream) < 0) { + if (!upstream) + return; upstream_is_gone = 1; - break; - default: - /* with base */ - break; } if (show_upstream_ref) { - ref = shorten_unambiguous_ref(branch->merge[0]->dst, 0); + ref = shorten_unambiguous_ref(upstream, 0); if (want_color(branch_use_color)) strbuf_addf(&fancy, "%s%s%s", branch_get_color(BRANCH_COLOR_UPSTREAM), @@@ -771,6 -763,7 +763,6 @@@ static const char edit_description[] = static int edit_branch_description(const char *branch_name) { - FILE *fp; int status; struct strbuf buf = STRBUF_INIT; struct strbuf name = STRBUF_INIT; @@@ -783,7 -776,8 +775,7 @@@ " %s\n" "Lines starting with '%c' will be stripped.\n", branch_name, comment_line_char); - fp = fopen(git_path(edit_description), "w"); - if ((fwrite(buf.buf, 1, buf.len, fp) < buf.len) || fclose(fp)) { + if (write_file(git_path(edit_description), 0, "%s", buf.buf)) { strbuf_release(&buf); return error(_("could not write branch description template: %s"), strerror(errno)); diff --combined builtin/log.c index 4c4e6be28c,6faeb82c8e..e67671e37a --- a/builtin/log.c +++ b/builtin/log.c @@@ -38,7 -38,7 +38,7 @@@ static const char *fmt_patch_subject_pr static const char *fmt_pretty; static const char * const builtin_log_usage[] = { - N_("git log [] [] [[--] ...]"), + N_("git log [] [] [[--] ...]"), N_("git show [] ..."), NULL }; @@@ -1632,16 -1632,13 +1632,13 @@@ int cmd_cherry(int argc, const char **a break; default: current_branch = branch_get(NULL); - if (!current_branch || !current_branch->merge - || !current_branch->merge[0] - || !current_branch->merge[0]->dst) { + upstream = branch_get_upstream(current_branch, NULL); + if (!upstream) { fprintf(stderr, _("Could not find a tracked" " remote branch, please" " specify manually.\n")); usage_with_options(cherry_usage, options); } - - upstream = current_branch->merge[0]->dst; } init_revisions(&revs, prefix); diff --combined builtin/merge.c index f89f60e11a,1840317118..85c54dcd5a --- a/builtin/merge.c +++ b/builtin/merge.c @@@ -492,7 -492,8 +492,7 @@@ static void merge_name(const char *remo } if (len) { struct strbuf truname = STRBUF_INIT; - strbuf_addstr(&truname, "refs/heads/"); - strbuf_addstr(&truname, remote); + strbuf_addf(&truname, "refs/heads/%s", remote); strbuf_setlen(&truname, truname.len - len); if (ref_exists(truname.buf)) { strbuf_addf(msg, @@@ -503,7 -504,28 +503,7 @@@ strbuf_release(&truname); goto cleanup; } - } - - if (!strcmp(remote, "FETCH_HEAD") && - !access(git_path("FETCH_HEAD"), R_OK)) { - const char *filename; - FILE *fp; - struct strbuf line = STRBUF_INIT; - char *ptr; - - filename = git_path("FETCH_HEAD"); - fp = fopen(filename, "r"); - if (!fp) - die_errno(_("could not open '%s' for reading"), - filename); - strbuf_getline(&line, fp, '\n'); - fclose(fp); - ptr = strstr(line.buf, "\tnot-for-merge\t"); - if (ptr) - strbuf_remove(&line, ptr-line.buf+1, 13); - strbuf_addbuf(msg, &line); - strbuf_release(&line); - goto cleanup; + strbuf_release(&truname); } if (remote_head->util) { @@@ -933,7 -955,7 +933,7 @@@ static int setup_with_upstream(const ch if (!branch) die(_("No current branch.")); - if (!branch->remote) + if (!branch->remote_name) die(_("No remote for the current branch.")); if (!branch->merge_nr) die(_("No default upstream defined for the current branch.")); @@@ -1015,24 -1037,28 +1015,24 @@@ static int default_edit_option(void st_stdin.st_mode == st_stdout.st_mode); } -static struct commit_list *collect_parents(struct commit *head_commit, - int *head_subsumed, - int argc, const char **argv) +static struct commit_list *reduce_parents(struct commit *head_commit, + int *head_subsumed, + struct commit_list *remoteheads) { - int i; - struct commit_list *remoteheads = NULL, *parents, *next; - struct commit_list **remotes = &remoteheads; + struct commit_list *parents, *next, **remotes = &remoteheads; - if (head_commit) - remotes = &commit_list_insert(head_commit, remotes)->next; - for (i = 0; i < argc; i++) { - struct commit *commit = get_merge_parent(argv[i]); - if (!commit) - help_unknown_ref(argv[i], "merge", - "not something we can merge"); - remotes = &commit_list_insert(commit, remotes)->next; - } - *remotes = NULL; + /* + * Is the current HEAD reachable from another commit being + * merged? If so we do not want to record it as a parent of + * the resulting merge, unless --no-ff is given. We will flip + * this variable to 0 when we find HEAD among the independent + * tips being merged. + */ + *head_subsumed = 1; + /* Find what parents to record by checking independent ones. */ parents = reduce_heads(remoteheads); - *head_subsumed = 1; /* we will flip this to 0 when we find it */ for (remoteheads = NULL, remotes = &remoteheads; parents; parents = next) { @@@ -1042,122 -1068,10 +1042,122 @@@ *head_subsumed = 0; else remotes = &commit_list_insert(commit, remotes)->next; + free(parents); } return remoteheads; } +static void prepare_merge_message(struct strbuf *merge_names, struct strbuf *merge_msg) +{ + struct fmt_merge_msg_opts opts; + + memset(&opts, 0, sizeof(opts)); + opts.add_title = !have_message; + opts.shortlog_len = shortlog_len; + opts.credit_people = (0 < option_edit); + + fmt_merge_msg(merge_names, merge_msg, &opts); + if (merge_msg->len) + strbuf_setlen(merge_msg, merge_msg->len - 1); +} + +static void handle_fetch_head(struct commit_list **remotes, struct strbuf *merge_names) +{ + const char *filename; + int fd, pos, npos; + struct strbuf fetch_head_file = STRBUF_INIT; + + if (!merge_names) + merge_names = &fetch_head_file; + + filename = git_path("FETCH_HEAD"); + fd = open(filename, O_RDONLY); + if (fd < 0) + die_errno(_("could not open '%s' for reading"), filename); + + if (strbuf_read(merge_names, fd, 0) < 0) + die_errno(_("could not read '%s'"), filename); + if (close(fd) < 0) + die_errno(_("could not close '%s'"), filename); + + for (pos = 0; pos < merge_names->len; pos = npos) { + unsigned char sha1[20]; + char *ptr; + struct commit *commit; + + ptr = strchr(merge_names->buf + pos, '\n'); + if (ptr) + npos = ptr - merge_names->buf + 1; + else + npos = merge_names->len; + + if (npos - pos < 40 + 2 || + get_sha1_hex(merge_names->buf + pos, sha1)) + commit = NULL; /* bad */ + else if (memcmp(merge_names->buf + pos + 40, "\t\t", 2)) + continue; /* not-for-merge */ + else { + char saved = merge_names->buf[pos + 40]; + merge_names->buf[pos + 40] = '\0'; + commit = get_merge_parent(merge_names->buf + pos); + merge_names->buf[pos + 40] = saved; + } + if (!commit) { + if (ptr) + *ptr = '\0'; + die("not something we can merge in %s: %s", + filename, merge_names->buf + pos); + } + remotes = &commit_list_insert(commit, remotes)->next; + } + + if (merge_names == &fetch_head_file) + strbuf_release(&fetch_head_file); +} + +static struct commit_list *collect_parents(struct commit *head_commit, + int *head_subsumed, + int argc, const char **argv, + struct strbuf *merge_msg) +{ + int i; + struct commit_list *remoteheads = NULL; + struct commit_list **remotes = &remoteheads; + struct strbuf merge_names = STRBUF_INIT, *autogen = NULL; + + if (merge_msg && (!have_message || shortlog_len)) + autogen = &merge_names; + + if (head_commit) + remotes = &commit_list_insert(head_commit, remotes)->next; + + if (argc == 1 && !strcmp(argv[0], "FETCH_HEAD")) { + handle_fetch_head(remotes, autogen); + remoteheads = reduce_parents(head_commit, head_subsumed, remoteheads); + } else { + for (i = 0; i < argc; i++) { + struct commit *commit = get_merge_parent(argv[i]); + if (!commit) + help_unknown_ref(argv[i], "merge", + "not something we can merge"); + remotes = &commit_list_insert(commit, remotes)->next; + } + remoteheads = reduce_parents(head_commit, head_subsumed, remoteheads); + if (autogen) { + struct commit_list *p; + for (p = remoteheads; p; p = p->next) + merge_name(merge_remote_util(p->item)->name, autogen); + } + } + + if (autogen) { + prepare_merge_message(autogen, merge_msg); + strbuf_release(autogen); + } + + return remoteheads; +} + int cmd_merge(int argc, const char **argv, const char *prefix) { unsigned char result_tree[20]; @@@ -1244,62 -1158,61 +1244,62 @@@ option_commit = 0; } - if (!abort_current_merge) { - if (!argc) { - if (default_to_upstream) - argc = setup_with_upstream(&argv); - else - die(_("No commit specified and merge.defaultToUpstream not set.")); - } else if (argc == 1 && !strcmp(argv[0], "-")) - argv[0] = "@{-1}"; + if (!argc) { + if (default_to_upstream) + argc = setup_with_upstream(&argv); + else + die(_("No commit specified and merge.defaultToUpstream not set.")); + } else if (argc == 1 && !strcmp(argv[0], "-")) { + argv[0] = "@{-1}"; } + if (!argc) usage_with_options(builtin_merge_usage, builtin_merge_options); - /* - * This could be traditional "merge HEAD ..." and - * the way we can tell it is to see if the second token is HEAD, - * but some people might have misused the interface and used a - * commit-ish that is the same as HEAD there instead. - * Traditional format never would have "-m" so it is an - * additional safety measure to check for it. - */ - - if (!have_message && head_commit && - is_old_style_invocation(argc, argv, head_commit->object.sha1)) { - strbuf_addstr(&merge_msg, argv[0]); - head_arg = argv[1]; - argv += 2; - argc -= 2; - remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv); - } else if (!head_commit) { + if (!head_commit) { struct commit *remote_head; /* * If the merged head is a valid one there is no reason * to forbid "git merge" into a branch yet to be born. * We do the same for "git pull". */ - if (argc != 1) - die(_("Can merge only exactly one commit into " - "empty head")); if (squash) die(_("Squash commit into empty head not supported yet")); if (fast_forward == FF_NO) die(_("Non-fast-forward commit does not make sense into " "an empty head")); - remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv); + remoteheads = collect_parents(head_commit, &head_subsumed, + argc, argv, NULL); remote_head = remoteheads->item; if (!remote_head) die(_("%s - not something we can merge"), argv[0]); + if (remoteheads->next) + die(_("Can merge only exactly one commit into empty head")); read_empty(remote_head->object.sha1, 0); update_ref("initial pull", "HEAD", remote_head->object.sha1, NULL, 0, UPDATE_REFS_DIE_ON_ERR); goto done; - } else { - struct strbuf merge_names = STRBUF_INIT; + } + /* + * This could be traditional "merge HEAD ..." and + * the way we can tell it is to see if the second token is HEAD, + * but some people might have misused the interface and used a + * commit-ish that is the same as HEAD there instead. + * Traditional format never would have "-m" so it is an + * additional safety measure to check for it. + */ + if (!have_message && + is_old_style_invocation(argc, argv, head_commit->object.sha1)) { + warning("old-style 'git merge HEAD ' is deprecated."); + strbuf_addstr(&merge_msg, argv[0]); + head_arg = argv[1]; + argv += 2; + argc -= 2; + remoteheads = collect_parents(head_commit, &head_subsumed, + argc, argv, NULL); + } else { /* We are invoked directly as the first-class UI. */ head_arg = "HEAD"; @@@ -1308,8 -1221,21 +1308,8 @@@ * the standard merge summary message to be appended * to the given message. */ - remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv); - for (p = remoteheads; p; p = p->next) - merge_name(merge_remote_util(p->item)->name, &merge_names); - - if (!have_message || shortlog_len) { - struct fmt_merge_msg_opts opts; - memset(&opts, 0, sizeof(opts)); - opts.add_title = !have_message; - opts.shortlog_len = shortlog_len; - opts.credit_people = (0 < option_edit); - - fmt_merge_msg(&merge_names, &merge_msg, &opts); - if (merge_msg.len) - strbuf_setlen(&merge_msg, merge_msg.len - 1); - } + remoteheads = collect_parents(head_commit, &head_subsumed, + argc, argv, &merge_msg); } if (!head_commit || !argc) diff --combined sha1_name.c index 46218ba02c,10969435bb..4f3c142c7a --- a/sha1_name.c +++ b/sha1_name.c @@@ -6,7 -6,6 +6,7 @@@ #include "tree-walk.h" #include "refs.h" #include "remote.h" +#include "dir.h" static int get_sha1_oneline(const char *, unsigned char *, struct commit_list *); @@@ -416,12 -415,12 +416,12 @@@ static int ambiguous_path(const char *p return slash; } - static inline int upstream_mark(const char *string, int len) + static inline int at_mark(const char *string, int len, + const char **suffix, int nr) { - const char *suffix[] = { "@{upstream}", "@{u}" }; int i; - for (i = 0; i < ARRAY_SIZE(suffix); i++) { + for (i = 0; i < nr; i++) { int suffix_len = strlen(suffix[i]); if (suffix_len <= len && !memcmp(string, suffix[i], suffix_len)) @@@ -430,6 -429,18 +430,18 @@@ return 0; } + static inline int upstream_mark(const char *string, int len) + { + const char *suffix[] = { "@{upstream}", "@{u}" }; + return at_mark(string, len, suffix, ARRAY_SIZE(suffix)); + } + + static inline int push_mark(const char *string, int len) + { + const char *suffix[] = { "@{push}" }; + return at_mark(string, len, suffix, ARRAY_SIZE(suffix)); + } + static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned lookup_flags); static int interpret_nth_prior_checkout(const char *name, int namelen, struct strbuf *buf); @@@ -477,7 -488,8 +489,8 @@@ static int get_sha1_basic(const char *s nth_prior = 1; continue; } - if (!upstream_mark(str + at, len - at)) { + if (!upstream_mark(str + at, len - at) && + !push_mark(str + at, len - at)) { reflog_len = (len-1) - (at+2); len = at; } @@@ -1056,46 -1068,36 +1069,36 @@@ static void set_shortened_ref(struct st free(s); } - static const char *get_upstream_branch(const char *branch_buf, int len) - { - char *branch = xstrndup(branch_buf, len); - struct branch *upstream = branch_get(*branch ? branch : NULL); - - /* - * Upstream can be NULL only if branch refers to HEAD and HEAD - * points to something different than a branch. - */ - if (!upstream) - die(_("HEAD does not point to a branch")); - if (!upstream->merge || !upstream->merge[0]->dst) { - if (!ref_exists(upstream->refname)) - die(_("No such branch: '%s'"), branch); - if (!upstream->merge) { - die(_("No upstream configured for branch '%s'"), - upstream->name); - } - die( - _("Upstream branch '%s' not stored as a remote-tracking branch"), - upstream->merge[0]->src); - } - free(branch); - - return upstream->merge[0]->dst; - } - - static int interpret_upstream_mark(const char *name, int namelen, - int at, struct strbuf *buf) + static int interpret_branch_mark(const char *name, int namelen, + int at, struct strbuf *buf, + int (*get_mark)(const char *, int), + const char *(*get_data)(struct branch *, + struct strbuf *)) { int len; + struct branch *branch; + struct strbuf err = STRBUF_INIT; + const char *value; - len = upstream_mark(name + at, namelen - at); + len = get_mark(name + at, namelen - at); if (!len) return -1; if (memchr(name, ':', at)) return -1; - set_shortened_ref(buf, get_upstream_branch(name, at)); + if (at) { + char *name_str = xmemdupz(name, at); + branch = branch_get(name_str); + free(name_str); + } else + branch = branch_get(NULL); + + value = get_data(branch, &err); + if (!value) + die("%s", err.buf); + + set_shortened_ref(buf, value); return len + at; } @@@ -1146,7 -1148,13 +1149,13 @@@ int interpret_branch_name(const char *n if (len > 0) return reinterpret(name, namelen, len, buf); - len = interpret_upstream_mark(name, namelen, at - name, buf); + len = interpret_branch_mark(name, namelen, at - name, buf, + upstream_mark, branch_get_upstream); + if (len > 0) + return len; + + len = interpret_branch_mark(name, namelen, at - name, buf, + push_mark, branch_get_push); if (len > 0) return len; } @@@ -1238,13 -1246,14 +1247,13 @@@ static void diagnose_invalid_sha1_path( const char *object_name, int object_name_len) { - struct stat st; unsigned char sha1[20]; unsigned mode; if (!prefix) prefix = ""; - if (!lstat(filename, &st)) + if (file_exists(filename)) die("Path '%s' exists on disk, but not in '%.*s'.", filename, object_name_len, object_name); if (errno == ENOENT || errno == ENOTDIR) { @@@ -1271,6 -1280,7 +1280,6 @@@ static void diagnose_invalid_index_path const char *prefix, const char *filename) { - struct stat st; const struct cache_entry *ce; int pos; unsigned namelen = strlen(filename); @@@ -1313,7 -1323,7 +1322,7 @@@ ce_stage(ce), filename); } - if (!lstat(filename, &st)) + if (file_exists(filename)) die("Path '%s' exists on disk, but not in the index.", filename); if (errno == ENOENT || errno == ENOTDIR) die("Path '%s' does not exist (neither on disk nor in the index).", @@@ -1433,19 -1443,11 +1442,19 @@@ static int get_sha1_with_context_1(cons new_filename = resolve_relative_path(filename); if (new_filename) filename = new_filename; - ret = get_tree_entry(tree_sha1, filename, sha1, &oc->mode); - if (ret && only_to_die) { - diagnose_invalid_sha1_path(prefix, filename, - tree_sha1, - name, len); + if (flags & GET_SHA1_FOLLOW_SYMLINKS) { + ret = get_tree_entry_follow_symlinks(tree_sha1, + filename, sha1, &oc->symlink_path, + &oc->mode); + } else { + ret = get_tree_entry(tree_sha1, filename, + sha1, &oc->mode); + if (ret && only_to_die) { + diagnose_invalid_sha1_path(prefix, + filename, + tree_sha1, + name, len); + } } hashcpy(oc->tree, tree_sha1); strlcpy(oc->path, filename, sizeof(oc->path)); @@@ -1476,7 -1478,5 +1485,7 @@@ void maybe_die_on_misspelt_object_name( int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc) { + if (flags & GET_SHA1_FOLLOW_SYMLINKS && flags & GET_SHA1_ONLY_TO_DIE) + die("BUG: incompatible flags for get_sha1_with_context"); return get_sha1_with_context_1(str, flags, NULL, sha1, orc); } diff --combined wt-status.c index 33452f169d,4313b9d845..c56c78fb6f --- a/wt-status.c +++ b/wt-status.c @@@ -585,8 -585,6 +585,8 @@@ static void wt_status_collect_untracked DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES; if (s->show_ignored_files) dir.flags |= DIR_SHOW_IGNORED_TOO; + else + dir.untracked = the_index.untracked; setup_standard_excludes(&dir); fill_directory(&dir, &s->pathspec); @@@ -1534,25 -1532,18 +1534,19 @@@ static void wt_shortstatus_print_tracki color_fprintf(s->fp, branch_color_local, "%s", branch_name); - switch (stat_tracking_info(branch, &num_ours, &num_theirs)) { - case 0: - /* no base */ - fputc(s->null_termination ? '\0' : '\n', s->fp); - return; - case -1: - /* with "gone" base */ + if (stat_tracking_info(branch, &num_ours, &num_theirs, &base) < 0) { + if (!base) { + fputc(s->null_termination ? '\0' : '\n', s->fp); + return; + } + upstream_is_gone = 1; - break; - default: - /* with base */ - break; } - base = branch->merge[0]->dst; base = shorten_unambiguous_ref(base, 0); color_fprintf(s->fp, header_color, "..."); color_fprintf(s->fp, branch_color_remote, "%s", base); + free((char *)base); if (!upstream_is_gone && !num_ours && !num_theirs) { fputc(s->null_termination ? '\0' : '\n', s->fp);