diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index 0363d0039b2740..f1f2a3f7ea9228 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -12,7 +12,7 @@ SYNOPSIS 'git clone' [--template=] [-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror] [-o ] [-b ] [-u ] [--reference ] - [--separate-git-dir ] + [--dissociate] [--separate-git-dir ] [--depth ] [--[no-]single-branch] [--recursive | --recurse-submodules] [--] [] @@ -98,7 +98,14 @@ objects from the source repository into a pack in the cloned repository. require fewer objects to be copied from the repository being cloned, reducing network and local storage costs. + -*NOTE*: see the NOTE for the `--shared` option. +*NOTE*: see the NOTE for the `--shared` option, and also the +`--dissociate` option. + +--dissociate:: + Borrow the objects from reference repositories specified + with the `--reference` options only to reduce network + transfer and stop borrowing from them after a clone is made + by making necessary local copies of borrowed objects. --quiet:: -q:: diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt index c7c0d21429bb74..77aacf13093643 100644 --- a/Documentation/git-imap-send.txt +++ b/Documentation/git-imap-send.txt @@ -9,7 +9,7 @@ git-imap-send - Send a collection of patches from stdin to an IMAP folder SYNOPSIS -------- [verse] -'git imap-send' +'git imap-send' [-v] [-q] [--[no-]curl] DESCRIPTION @@ -26,6 +26,27 @@ Typical usage is something like: git format-patch --signoff --stdout --attach origin | git imap-send +OPTIONS +------- + +-v:: +--verbose:: + Be verbose. + +-q:: +--quiet:: + Be quiet. + +--curl:: + Use libcurl to communicate with the IMAP server, unless tunneling + into it. Ignored if Git was built without the USE_CURL_FOR_IMAP_SEND + option set. + +--no-curl:: + Talk to the IMAP server using git's own IMAP routines instead of + using libcurl. + + CONFIGURATION ------------- @@ -75,7 +96,9 @@ imap.preformattedHTML:: imap.authMethod:: Specify authenticate method for authentication with IMAP server. - Current supported method is 'CRAM-MD5' only. If this is not set + If Git was built with the NO_CURL option, or if your curl version is older + than 7.34.0, or if you're running git-imap-send with the `--no-curl` + option, the only supported method is 'CRAM-MD5'. If this is not set then 'git imap-send' uses the basic IMAP plaintext LOGIN command. Examples diff --git a/INSTALL b/INSTALL index 6ec7a24e1a7ff1..ffb071e9f03a79 100644 --- a/INSTALL +++ b/INSTALL @@ -108,18 +108,21 @@ Issues of note: so you might need to install additional packages other than Perl itself, e.g. Time::HiRes. - - "openssl" library is used by git-imap-send to use IMAP over SSL. - If you don't need it, use NO_OPENSSL. + - git-imap-send needs the OpenSSL library to talk IMAP over SSL if + you are using libcurl older than 7.34.0. Otherwise you can use + NO_OPENSSL without losing git-imap-send. By default, git uses OpenSSL for SHA1 but it will use its own library (inspired by Mozilla's) with either NO_OPENSSL or BLK_SHA1. Also included is a version optimized for PowerPC (PPC_SHA1). - - "libcurl" library is used by git-http-fetch and git-fetch. You - might also want the "curl" executable for debugging purposes. - If you do not use http:// or https:// repositories, you do not - have to have them (use NO_CURL). + - "libcurl" library is used by git-http-fetch, git-fetch, and, if + the curl version >= 7.34.0, for git-imap-send. You might also + want the "curl" executable for debugging purposes. If you do not + use http:// or https:// repositories, and do not want to put + patches into an IMAP mailbox, you do not have to have them + (use NO_CURL). - "expat" library; git-http-push uses it for remote lock management over DAV. Similar to "curl" above, this is optional diff --git a/Makefile b/Makefile index 827006ba611c7e..14d5ac109e3296 100644 --- a/Makefile +++ b/Makefile @@ -995,6 +995,9 @@ ifdef HAVE_ALLOCA_H BASIC_CFLAGS += -DHAVE_ALLOCA_H endif +IMAP_SEND_BUILDDEPS = +IMAP_SEND_LDFLAGS = $(OPENSSL_LINK) $(OPENSSL_LIBSSL) $(LIB_4_CRYPTO) + ifdef NO_CURL BASIC_CFLAGS += -DNO_CURL REMOTE_CURL_PRIMARY = @@ -1029,6 +1032,15 @@ else PROGRAM_OBJS += http-push.o endif endif + curl_check := $(shell (echo 072200; curl-config --vernum) 2>/dev/null | sort -r | sed -ne 2p) + ifeq "$(curl_check)" "072200" + USE_CURL_FOR_IMAP_SEND = YesPlease + endif + ifdef USE_CURL_FOR_IMAP_SEND + BASIC_CFLAGS += -DUSE_CURL_FOR_IMAP_SEND + IMAP_SEND_BUILDDEPS = http.o + IMAP_SEND_LDFLAGS += $(CURL_LIBCURL) + endif ifndef NO_EXPAT ifdef EXPATDIR BASIC_CFLAGS += -I$(EXPATDIR)/include @@ -1874,7 +1886,7 @@ gettext.sp gettext.s gettext.o: GIT-PREFIX gettext.sp gettext.s gettext.o: EXTRA_CPPFLAGS = \ -DGIT_LOCALE_PATH='"$(localedir_SQ)"' -http-push.sp http.sp http-walker.sp remote-curl.sp: SPARSE_FLAGS += \ +http-push.sp http.sp http-walker.sp remote-curl.sp imap-send.sp: SPARSE_FLAGS += \ -DCURL_DISABLE_TYPECHECK ifdef NO_EXPAT @@ -1895,9 +1907,9 @@ endif git-%$X: %.o GIT-LDFLAGS $(GITLIBS) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) -git-imap-send$X: imap-send.o GIT-LDFLAGS $(GITLIBS) +git-imap-send$X: imap-send.o $(IMAP_SEND_BUILDDEPS) GIT-LDFLAGS $(GITLIBS) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ - $(LIBS) $(OPENSSL_LINK) $(OPENSSL_LIBSSL) $(LIB_4_CRYPTO) + $(LIBS) $(IMAP_SEND_LDFLAGS) git-http-fetch$X: http.o http-walker.o http-fetch.o GIT-LDFLAGS $(GITLIBS) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ diff --git a/bisect.c b/bisect.c index df09cbc8cabe9d..8c6d843699ab04 100644 --- a/bisect.c +++ b/bisect.c @@ -777,7 +777,7 @@ static void check_merge_bases(int no_checkout) int rev_nr; struct commit **rev = get_bad_and_good_commits(&rev_nr); - result = get_merge_bases_many(rev[0], rev_nr - 1, rev + 1, 0); + result = get_merge_bases_many(rev[0], rev_nr - 1, rev + 1); for (; result; result = result->next) { const unsigned char *mb = result->item->object.sha1; diff --git a/builtin/clone.c b/builtin/clone.c index d5e7532105aa66..316c75d0b342d0 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -49,6 +49,7 @@ static int option_verbosity; static int option_progress = -1; static struct string_list option_config; static struct string_list option_reference; +static int option_dissociate; static int opt_parse_reference(const struct option *opt, const char *arg, int unset) { @@ -94,6 +95,8 @@ static struct option builtin_clone_options[] = { N_("create a shallow clone of that depth")), OPT_BOOL(0, "single-branch", &option_single_branch, N_("clone only one branch, HEAD or --branch")), + OPT_BOOL(0, "dissociate", &option_dissociate, + N_("use --reference only while cloning")), OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"), N_("separate git dir from working tree")), OPT_STRING_LIST('c', "config", &option_config, N_("key=value"), @@ -735,6 +738,16 @@ static void write_refspec_config(const char *src_ref_prefix, strbuf_release(&value); } +static void dissociate_from_references(void) +{ + static const char* argv[] = { "repack", "-a", "-d", NULL }; + + if (run_command_v_opt(argv, RUN_GIT_CMD|RUN_COMMAND_NO_STDIN)) + die(_("cannot repack to clean up")); + if (unlink(git_path("objects/info/alternates")) && errno != ENOENT) + die_errno(_("cannot unlink temporary alternates file")); +} + int cmd_clone(int argc, const char **argv, const char *prefix) { int is_bundle = 0, is_local; @@ -880,6 +893,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (option_reference.nr) setup_reference(); + else if (option_dissociate) { + warning(_("--dissociate given, but there is no --reference")); + option_dissociate = 0; + } fetch_pattern = value.buf; refspec = parse_fetch_refspec(1, &fetch_pattern); @@ -993,6 +1010,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix) transport_unlock_pack(transport); transport_disconnect(transport); + if (option_dissociate) + dissociate_from_references(); + junk_mode = JUNK_LEAVE_REPO; err = checkout(); diff --git a/builtin/commit.c b/builtin/commit.c index e108c5301564a8..e3c60dd2e8403b 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -677,6 +677,52 @@ static void adjust_comment_line_char(const struct strbuf *sb) comment_line_char = *p; } +/* + * Inspect sb and determine the true "end" of the log message, in + * order to find where to put a new Signed-off-by: line. Ignored are + * trailing comment lines and blank lines, and also the traditional + * "Conflicts:" block that is not commented out, so that we can use + * "git commit -s --amend" on an existing commit that forgot to remove + * it. + * + * Returns the number of bytes from the tail to ignore, to be fed as + * the second parameter to append_signoff(). + */ +static int ignore_non_trailer(struct strbuf *sb) +{ + int boc = 0; + int bol = 0; + int in_old_conflicts_block = 0; + + while (bol < sb->len) { + char *next_line; + + if (!(next_line = memchr(sb->buf + bol, '\n', sb->len - bol))) + next_line = sb->buf + sb->len; + else + next_line++; + + if (sb->buf[bol] == comment_line_char || sb->buf[bol] == '\n') { + /* is this the first of the run of comments? */ + if (!boc) + boc = bol; + /* otherwise, it is just continuing */ + } else if (starts_with(sb->buf + bol, "Conflicts:\n")) { + in_old_conflicts_block = 1; + if (!boc) + boc = bol; + } else if (in_old_conflicts_block && sb->buf[bol] == '\t') { + ; /* a pathname in the conflicts block */ + } else if (boc) { + /* the previous was not trailing comment */ + boc = 0; + in_old_conflicts_block = 0; + } + bol = next_line - sb->buf; + } + return boc ? sb->len - boc : 0; +} + static int prepare_to_commit(const char *index_file, const char *prefix, struct commit *current_head, struct wt_status *s, @@ -800,32 +846,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix, if (clean_message_contents) stripspace(&sb, 0); - if (signoff) { - /* - * See if we have a Conflicts: block at the end. If yes, count - * its size, so we can ignore it. - */ - int ignore_footer = 0; - int i, eol, previous = 0; - const char *nl; - - for (i = 0; i < sb.len; i++) { - nl = memchr(sb.buf + i, '\n', sb.len - i); - if (nl) - eol = nl - sb.buf; - else - eol = sb.len; - if (starts_with(sb.buf + previous, "\nConflicts:\n")) { - ignore_footer = sb.len - previous; - break; - } - while (i < eol) - i++; - previous = eol; - } - - append_signoff(&sb, ignore_footer, 0); - } + if (signoff) + append_signoff(&sb, ignore_non_trailer(&sb), 0); if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len) die_errno(_("could not write commit template")); diff --git a/builtin/index-pack.c b/builtin/index-pack.c index a369f553535164..46321176719808 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -447,7 +447,7 @@ static void *unpack_entry_data(unsigned long offset, unsigned long size, if (type == OBJ_BLOB && size > big_file_threshold) buf = fixed_buf; else - buf = xmalloc(size); + buf = xmallocz(size); memset(&stream, 0, sizeof(stream)); git_inflate_init(&stream); @@ -552,7 +552,7 @@ static void *unpack_data(struct object_entry *obj, git_zstream stream; int status; - data = xmalloc(consume ? 64*1024 : obj->size); + data = xmallocz(consume ? 64*1024 : obj->size); inbuf = xmalloc((len < 64*1024) ? len : 64*1024); memset(&stream, 0, sizeof(stream)); diff --git a/builtin/merge-base.c b/builtin/merge-base.c index 0ecde8da306159..fdebef6fa157e9 100644 --- a/builtin/merge-base.c +++ b/builtin/merge-base.c @@ -10,7 +10,7 @@ static int show_merge_base(struct commit **rev, int rev_nr, int show_all) { struct commit_list *result; - result = get_merge_bases_many(rev[0], rev_nr - 1, rev + 1, 0); + result = get_merge_bases_many_dirty(rev[0], rev_nr - 1, rev + 1); if (!result) return 1; @@ -176,7 +176,7 @@ static int handle_fork_point(int argc, const char **argv) for (i = 0; i < revs.nr; i++) revs.commit[i]->object.flags &= ~TMP_MARK; - bases = get_merge_bases_many(derived, revs.nr, revs.commit, 0); + bases = get_merge_bases_many_dirty(derived, revs.nr, revs.commit); /* * There should be one and only one merge base, when we found diff --git a/builtin/merge.c b/builtin/merge.c index bebbe5b3081ebe..9effed7ff15d08 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -29,6 +29,7 @@ #include "remote.h" #include "fmt-merge-msg.h" #include "gpg-interface.h" +#include "sequencer.h" #define DEFAULT_TWOHEAD (1<<0) #define DEFAULT_OCTOPUS (1<<1) @@ -880,28 +881,19 @@ static int finish_automerge(struct commit *head, return 0; } -static int suggest_conflicts(int renormalizing) +static int suggest_conflicts(void) { const char *filename; FILE *fp; - int pos; + struct strbuf msgbuf = STRBUF_INIT; filename = git_path("MERGE_MSG"); fp = fopen(filename, "a"); if (!fp) die_errno(_("Could not open '%s' for writing"), filename); - fprintf(fp, "\nConflicts:\n"); - for (pos = 0; pos < active_nr; pos++) { - const struct cache_entry *ce = active_cache[pos]; - - if (ce_stage(ce)) { - fprintf(fp, "\t%s\n", ce->name); - while (pos + 1 < active_nr && - !strcmp(ce->name, - active_cache[pos + 1]->name)) - pos++; - } - } + + append_conflicts_hint(&msgbuf); + fputs(msgbuf.buf, fp); fclose(fp); rerere(allow_rerere_auto); printf(_("Automatic merge failed; " @@ -1320,7 +1312,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (!remoteheads) ; /* already up-to-date */ else if (!remoteheads->next) - common = get_merge_bases(head_commit, remoteheads->item, 1); + common = get_merge_bases(head_commit, remoteheads->item); else { struct commit_list *list = remoteheads; commit_list_insert(head_commit, &list); @@ -1417,7 +1409,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) * merge_bases again, otherwise "git merge HEAD^ * HEAD^^" would be missed. */ - common_one = get_merge_bases(head_commit, j->item, 1); + common_one = get_merge_bases(head_commit, j->item); if (hashcmp(common_one->item->object.sha1, j->item->object.sha1)) { up_to_date = 0; @@ -1550,7 +1542,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) fprintf(stderr, _("Automatic merge went well; " "stopped before committing as requested\n")); else - ret = suggest_conflicts(option_renormalize); + ret = suggest_conflicts(); done: free(branch_to_free); diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index 35d3c43ed656bf..95328b80d93026 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -279,7 +279,7 @@ static int try_difference(const char *arg) struct commit *a, *b; a = lookup_commit_reference(sha1); b = lookup_commit_reference(end); - exclude = get_merge_bases(a, b, 1); + exclude = get_merge_bases(a, b); while (exclude) { struct commit_list *n = exclude->next; show_rev(REVERSED, diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c index 855d94b90ba019..ac6667242c562b 100644 --- a/builtin/unpack-objects.c +++ b/builtin/unpack-objects.c @@ -91,7 +91,7 @@ static void use(int bytes) static void *get_data(unsigned long size) { git_zstream stream; - void *buf = xmalloc(size); + void *buf = xmallocz(size); memset(&stream, 0, sizeof(stream)); diff --git a/commit.c b/commit.c index 19cf8f9c67bd0f..d13250eccce5d3 100644 --- a/commit.c +++ b/commit.c @@ -867,7 +867,7 @@ struct commit_list *get_octopus_merge_bases(struct commit_list *in) for (j = ret; j; j = j->next) { struct commit_list *bases; - bases = get_merge_bases(i->item, j->item, 1); + bases = get_merge_bases(i->item, j->item); if (!new) new = bases; else @@ -936,10 +936,10 @@ static int remove_redundant(struct commit **array, int cnt) return filled; } -struct commit_list *get_merge_bases_many(struct commit *one, - int n, - struct commit **twos, - int cleanup) +static struct commit_list *get_merge_bases_many_0(struct commit *one, + int n, + struct commit **twos, + int cleanup) { struct commit_list *list; struct commit **rslt; @@ -977,10 +977,23 @@ struct commit_list *get_merge_bases_many(struct commit *one, return result; } -struct commit_list *get_merge_bases(struct commit *one, struct commit *two, - int cleanup) +struct commit_list *get_merge_bases_many(struct commit *one, + int n, + struct commit **twos) +{ + return get_merge_bases_many_0(one, n, twos, 1); +} + +struct commit_list *get_merge_bases_many_dirty(struct commit *one, + int n, + struct commit **twos) +{ + return get_merge_bases_many_0(one, n, twos, 0); +} + +struct commit_list *get_merge_bases(struct commit *one, struct commit *two) { - return get_merge_bases_many(one, 1, &two, cleanup); + return get_merge_bases_many_0(one, 1, &two, 1); } /* diff --git a/commit.h b/commit.h index bc68ccbe691a28..28bc553b2d05a8 100644 --- a/commit.h +++ b/commit.h @@ -236,10 +236,13 @@ struct commit_graft *read_graft_line(char *buf, int len); int register_commit_graft(struct commit_graft *, int); struct commit_graft *lookup_commit_graft(const unsigned char *sha1); -extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup); -extern struct commit_list *get_merge_bases_many(struct commit *one, int n, struct commit **twos, int cleanup); +extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2); +extern struct commit_list *get_merge_bases_many(struct commit *one, int n, struct commit **twos); extern struct commit_list *get_octopus_merge_bases(struct commit_list *in); +/* To be used only when object flags after this call no longer matter */ +extern struct commit_list *get_merge_bases_many_dirty(struct commit *one, int n, struct commit **twos); + /* largest positive number a signed 32-bit integer can contain */ #define INFINITE_DEPTH 0x7fffffff diff --git a/diffcore-break.c b/diffcore-break.c index 1d9e530a84758e..5473493e105516 100644 --- a/diffcore-break.c +++ b/diffcore-break.c @@ -246,6 +246,13 @@ static void merge_broken(struct diff_filepair *p, dp = diff_queue(outq, d->one, c->two); dp->score = p->score; + /* + * We will be one extra user of the same src side of the + * broken pair, if it was used as the rename source for other + * paths elsewhere. Increment to mark that the path stays + * in the resulting tree. + */ + d->one->rename_used++; diff_free_filespec_data(d->two); diff_free_filespec_data(c->one); free(d); diff --git a/fsck.c b/fsck.c index 2fffa434a5763a..9165eb3b4c31e0 100644 --- a/fsck.c +++ b/fsck.c @@ -452,6 +452,9 @@ static int fsck_tag(struct tag *tag, const char *data, int fsck_object(struct object *obj, void *data, unsigned long size, int strict, fsck_error error_func) { + if (data && ((char *)data)[size]) + die("I got a non-NUL-terminated buffer!!!"); + if (!obj) return error_func(obj, FSCK_ERROR, "no valid object to fsck"); diff --git a/imap-send.c b/imap-send.c index 70bcc7a4e533bb..4dfe4c25d76649 100644 --- a/imap-send.c +++ b/imap-send.c @@ -26,11 +26,24 @@ #include "credential.h" #include "exec_cmd.h" #include "run-command.h" +#include "parse-options.h" #ifdef NO_OPENSSL typedef void *SSL; #endif +#ifdef USE_CURL_FOR_IMAP_SEND +#include "http.h" +#endif + +static int verbosity; +static int use_curl; /* strictly opt in */ -static const char imap_send_usage[] = "git imap-send < "; +static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] < ", NULL }; + +static struct option imap_send_options[] = { + OPT__VERBOSITY(&verbosity), + OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"), + OPT_END() +}; #undef DRV_OK #define DRV_OK 0 @@ -38,8 +51,6 @@ static const char imap_send_usage[] = "git imap-send < "; #define DRV_BOX_BAD -2 #define DRV_STORE_BAD -3 -static int Verbose, Quiet; - __attribute__((format (printf, 1, 2))) static void imap_info(const char *, ...); __attribute__((format (printf, 1, 2))) @@ -418,7 +429,7 @@ static int buffer_gets(struct imap_buffer *b, char **s) if (b->buf[b->offset + 1] == '\n') { b->buf[b->offset] = 0; /* terminate the string */ b->offset += 2; /* next line */ - if (Verbose) + if (0 < verbosity) puts(*s); return 0; } @@ -433,7 +444,7 @@ static void imap_info(const char *msg, ...) { va_list va; - if (!Quiet) { + if (0 <= verbosity) { va_start(va, msg); vprintf(msg, va); va_end(va); @@ -445,7 +456,7 @@ static void imap_warn(const char *msg, ...) { va_list va; - if (Quiet < 2) { + if (-2 < verbosity) { va_start(va, msg); vfprintf(stderr, msg, va); va_end(va); @@ -522,7 +533,7 @@ static struct imap_cmd *issue_imap_cmd(struct imap_store *ctx, cmd->tag, cmd->cmd, cmd->cb.dlen, CAP(LITERALPLUS) ? "+" : ""); - if (Verbose) { + if (0 < verbosity) { if (imap->num_in_progress) printf("(%d in progress) ", imap->num_in_progress); if (!starts_with(cmd->cmd, "LOGIN")) @@ -1338,26 +1349,166 @@ static void git_imap_config(void) git_config_get_string("imap.authmethod", &server.auth_method); } -int main(int argc, char **argv) +static int append_msgs_to_imap(struct imap_server_conf *server, + struct strbuf* all_msgs, int total) { - struct strbuf all_msgs = STRBUF_INIT; struct strbuf msg = STRBUF_INIT; struct imap_store *ctx = NULL; int ofs = 0; int r; - int total, n = 0; + int n = 0; + + ctx = imap_open_store(server, server->folder); + if (!ctx) { + fprintf(stderr, "failed to open store\n"); + return 1; + } + ctx->name = server->folder; + + fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : ""); + while (1) { + unsigned percent = n * 100 / total; + + fprintf(stderr, "%4u%% (%d/%d) done\r", percent, n, total); + + if (!split_msg(all_msgs, &msg, &ofs)) + break; + if (server->use_html) + wrap_in_html(&msg); + r = imap_store_msg(ctx, &msg); + if (r != DRV_OK) + break; + n++; + } + fprintf(stderr, "\n"); + + imap_close_store(ctx); + + return 0; +} + +#ifdef USE_CURL_FOR_IMAP_SEND +static CURL *setup_curl(struct imap_server_conf *srvc) +{ + CURL *curl; + struct strbuf path = STRBUF_INIT; + + if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) + die("curl_global_init failed"); + + curl = curl_easy_init(); + + if (!curl) + die("curl_easy_init failed"); + + curl_easy_setopt(curl, CURLOPT_USERNAME, server.user); + curl_easy_setopt(curl, CURLOPT_PASSWORD, server.pass); + + strbuf_addstr(&path, server.host); + if (!path.len || path.buf[path.len - 1] != '/') + strbuf_addch(&path, '/'); + strbuf_addstr(&path, server.folder); + + curl_easy_setopt(curl, CURLOPT_URL, path.buf); + strbuf_release(&path); + curl_easy_setopt(curl, CURLOPT_PORT, server.port); + + if (server.auth_method) { + struct strbuf auth = STRBUF_INIT; + strbuf_addstr(&auth, "AUTH="); + strbuf_addstr(&auth, server.auth_method); + curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf); + strbuf_release(&auth); + } + + if (server.use_ssl) + curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL); + + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, server.ssl_verify); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, server.ssl_verify); + + curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer); + + curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); + + if (0 < verbosity) + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + + return curl; +} + +static int curl_append_msgs_to_imap(struct imap_server_conf *server, + struct strbuf* all_msgs, int total) { + int ofs = 0; + int n = 0; + struct buffer msgbuf = { STRBUF_INIT, 0 }; + CURL *curl; + CURLcode res = CURLE_OK; + + curl = setup_curl(server); + curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf); + + fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : ""); + while (1) { + unsigned percent = n * 100 / total; + int prev_len; + + fprintf(stderr, "%4u%% (%d/%d) done\r", percent, n, total); + + prev_len = msgbuf.buf.len; + if (!split_msg(all_msgs, &msgbuf.buf, &ofs)) + break; + if (server->use_html) + wrap_in_html(&msgbuf.buf); + lf_to_crlf(&msgbuf.buf); + + curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, + (curl_off_t)(msgbuf.buf.len-prev_len)); + + res = curl_easy_perform(curl); + + if(res != CURLE_OK) { + fprintf(stderr, "curl_easy_perform() failed: %s\n", + curl_easy_strerror(res)); + break; + } + + n++; + } + fprintf(stderr, "\n"); + + curl_easy_cleanup(curl); + curl_global_cleanup(); + + return 0; +} +#endif + +int main(int argc, char **argv) +{ + struct strbuf all_msgs = STRBUF_INIT; + int total; int nongit_ok; git_extract_argv0_path(argv[0]); git_setup_gettext(); - if (argc != 1) - usage(imap_send_usage); - setup_git_directory_gently(&nongit_ok); git_imap_config(); + argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0); + + if (argc) + usage_with_options(imap_send_usage, imap_send_options); + +#ifndef USE_CURL_FOR_IMAP_SEND + if (use_curl) { + warning("--use-curl not supported in this build"); + use_curl = 0; + } +#endif + if (!server.port) server.port = server.use_ssl ? 993 : 143; @@ -1391,29 +1542,14 @@ int main(int argc, char **argv) } /* write it to the imap server */ - ctx = imap_open_store(&server, server.folder); - if (!ctx) { - fprintf(stderr, "failed to open store\n"); - return 1; - } - - fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : ""); - while (1) { - unsigned percent = n * 100 / total; - fprintf(stderr, "%4u%% (%d/%d) done\r", percent, n, total); - if (!split_msg(&all_msgs, &msg, &ofs)) - break; - if (server.use_html) - wrap_in_html(&msg); - r = imap_store_msg(ctx, &msg); - if (r != DRV_OK) - break; - n++; - } - fprintf(stderr, "\n"); + if (server.tunnel) + return append_msgs_to_imap(&server, &all_msgs, total); - imap_close_store(ctx); +#ifdef USE_CURL_FOR_IMAP_SEND + if (use_curl) + return curl_append_msgs_to_imap(&server, &all_msgs, total); +#endif - return 0; + return append_msgs_to_imap(&server, &all_msgs, total); } diff --git a/lockfile.c b/lockfile.c index 4f16ee78ce3dbc..98892777516948 100644 --- a/lockfile.c +++ b/lockfile.c @@ -128,9 +128,17 @@ static int lock_file(struct lock_file *lk, const char *path, int flags) path); } - strbuf_add(&lk->filename, path, pathlen); - if (!(flags & LOCK_NO_DEREF)) - resolve_symlink(&lk->filename); + if (flags & LOCK_NO_DEREF) { + strbuf_add_absolute_path(&lk->filename, path); + } else { + struct strbuf resolved_path = STRBUF_INIT; + + strbuf_add(&resolved_path, path, pathlen); + resolve_symlink(&resolved_path); + strbuf_add_absolute_path(&lk->filename, resolved_path.buf); + strbuf_release(&resolved_path); + } + strbuf_addstr(&lk->filename, LOCK_SUFFIX); lk->fd = open(lk->filename.buf, O_RDWR | O_CREAT | O_EXCL, 0666); if (lk->fd < 0) { diff --git a/merge-recursive.c b/merge-recursive.c index fdb7d0f10ba471..f758f7eca98a03 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -1904,7 +1904,7 @@ int merge_recursive(struct merge_options *o, } if (!ca) { - ca = get_merge_bases(h1, h2, 1); + ca = get_merge_bases(h1, h2); ca = reverse_commit_list(ca); } diff --git a/notes-merge.c b/notes-merge.c index 7eb9d7a0103ad8..109ff4ef410b77 100644 --- a/notes-merge.c +++ b/notes-merge.c @@ -594,7 +594,7 @@ int notes_merge(struct notes_merge_options *o, assert(local && remote); /* Find merge bases */ - bases = get_merge_bases(local, remote, 1); + bases = get_merge_bases(local, remote); if (!bases) { base_sha1 = null_sha1; base_tree_sha1 = EMPTY_TREE_SHA1_BIN; diff --git a/remote.c b/remote.c index f62421702f66bb..d13e69dbb65a79 100644 --- a/remote.c +++ b/remote.c @@ -1631,6 +1631,27 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror, } } +static void set_merge(struct branch *ret) +{ + char *ref; + unsigned char sha1[20]; + int i; + + ret->merge = xcalloc(ret->merge_nr, sizeof(*ret->merge)); + for (i = 0; i < ret->merge_nr; i++) { + ret->merge[i] = xcalloc(1, sizeof(**ret->merge)); + ret->merge[i]->src = xstrdup(ret->merge_name[i]); + if (!remote_find_tracking(ret->remote, ret->merge[i]) || + strcmp(ret->remote_name, ".")) + continue; + if (dwim_ref(ret->merge_name[i], strlen(ret->merge_name[i]), + sha1, &ref) == 1) + ret->merge[i]->dst = ref; + else + ret->merge[i]->dst = xstrdup(ret->merge_name[i]); + } +} + struct branch *branch_get(const char *name) { struct branch *ret; @@ -1642,17 +1663,8 @@ struct branch *branch_get(const char *name) ret = make_branch(name, 0); if (ret && ret->remote_name) { ret->remote = remote_get(ret->remote_name); - if (ret->merge_nr) { - int i; - ret->merge = xcalloc(ret->merge_nr, sizeof(*ret->merge)); - for (i = 0; i < ret->merge_nr; i++) { - ret->merge[i] = xcalloc(1, sizeof(**ret->merge)); - ret->merge[i]->src = xstrdup(ret->merge_name[i]); - if (remote_find_tracking(ret->remote, ret->merge[i]) - && !strcmp(ret->remote_name, ".")) - ret->merge[i]->dst = xstrdup(ret->merge_name[i]); - } - } + if (ret->merge_nr) + set_merge(ret); } return ret; } diff --git a/revision.c b/revision.c index 75dda928ea6be1..14e0e0358cf19d 100644 --- a/revision.c +++ b/revision.c @@ -1441,7 +1441,7 @@ static void prepare_show_merge(struct rev_info *revs) other = lookup_commit_or_die(sha1, "MERGE_HEAD"); add_pending_object(revs, &head->object, "HEAD"); add_pending_object(revs, &other->object, "MERGE_HEAD"); - bases = get_merge_bases(head, other, 1); + bases = get_merge_bases(head, other); add_rev_cmdline_list(revs, bases, REV_CMD_MERGE_BASE, UNINTERESTING | BOTTOM); add_pending_commit_list(revs, bases, UNINTERESTING | BOTTOM); free_commit_list(bases); @@ -1546,7 +1546,7 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi : lookup_commit_reference(b_obj->sha1)); if (!a || !b) goto missing; - exclude = get_merge_bases(a, b, 1); + exclude = get_merge_bases(a, b); add_rev_cmdline_list(revs, exclude, REV_CMD_MERGE_BASE, flags_exclude); diff --git a/sequencer.c b/sequencer.c index a03d4fa2521fbc..77a1266760718b 100644 --- a/sequencer.c +++ b/sequencer.c @@ -267,6 +267,23 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from, return 0; } +void append_conflicts_hint(struct strbuf *msgbuf) +{ + int i; + + strbuf_addch(msgbuf, '\n'); + strbuf_commented_addf(msgbuf, "Conflicts:\n"); + for (i = 0; i < active_nr;) { + const struct cache_entry *ce = active_cache[i++]; + if (ce_stage(ce)) { + strbuf_commented_addf(msgbuf, "\t%s\n", ce->name); + while (i < active_nr && !strcmp(ce->name, + active_cache[i]->name)) + i++; + } + } +} + static int do_recursive_merge(struct commit *base, struct commit *next, const char *base_label, const char *next_label, unsigned char *head, struct strbuf *msgbuf, @@ -307,21 +324,8 @@ static int do_recursive_merge(struct commit *base, struct commit *next, if (opts->signoff) append_signoff(msgbuf, 0, 0); - if (!clean) { - int i; - strbuf_addstr(msgbuf, "\nConflicts:\n"); - for (i = 0; i < active_nr;) { - const struct cache_entry *ce = active_cache[i++]; - if (ce_stage(ce)) { - strbuf_addch(msgbuf, '\t'); - strbuf_addstr(msgbuf, ce->name); - strbuf_addch(msgbuf, '\n'); - while (i < active_nr && !strcmp(ce->name, - active_cache[i]->name)) - i++; - } - } - } + if (!clean) + append_conflicts_hint(msgbuf); return !clean; } diff --git a/sequencer.h b/sequencer.h index db43e9cf86dcc0..5ed5cb1d97ced7 100644 --- a/sequencer.h +++ b/sequencer.h @@ -53,5 +53,6 @@ int sequencer_pick_revisions(struct replay_opts *opts); extern const char sign_off_header[]; void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag); +void append_conflicts_hint(struct strbuf *msgbuf); #endif diff --git a/sha1_name.c b/sha1_name.c index 5b004f513b999b..973755793663e5 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -993,7 +993,7 @@ int get_sha1_mb(const char *name, unsigned char *sha1) two = lookup_commit_reference_gently(sha1_tmp, 0); if (!two) return -1; - mbs = get_merge_bases(one, two, 1); + mbs = get_merge_bases(one, two); if (!mbs || mbs->next) st = -1; else { diff --git a/strbuf.c b/strbuf.c index 0346e74a47d14e..88cafd4a70b817 100644 --- a/strbuf.c +++ b/strbuf.c @@ -229,7 +229,8 @@ static void add_lines(struct strbuf *out, const char *next = memchr(buf, '\n', size); next = next ? (next + 1) : (buf + size); - prefix = (prefix2 && buf[0] == '\n') ? prefix2 : prefix1; + prefix = ((prefix2 && (buf[0] == '\n' || buf[0] == '\t')) + ? prefix2 : prefix1); strbuf_addstr(out, prefix); strbuf_add(out, buf, next - buf); size -= next - buf; diff --git a/submodule.c b/submodule.c index 0690dc50d07e9f..d37d400b2227c5 100644 --- a/submodule.c +++ b/submodule.c @@ -301,7 +301,7 @@ static int prepare_submodule_summary(struct rev_info *rev, const char *path, left->object.flags |= SYMMETRIC_LEFT; add_pending_object(rev, &left->object, path); add_pending_object(rev, &right->object, path); - merge_bases = get_merge_bases(left, right, 1); + merge_bases = get_merge_bases(left, right); if (merge_bases) { if (merge_bases->item == left) *fast_forward = 1; diff --git a/t/t0030-stripspace.sh b/t/t0030-stripspace.sh index 0333dd9875af9f..29e91d861cbd64 100755 --- a/t/t0030-stripspace.sh +++ b/t/t0030-stripspace.sh @@ -432,4 +432,10 @@ test_expect_success '-c with changed comment char' ' test_cmp expect actual ' +test_expect_success 'avoid SP-HT sequence in commented line' ' + printf "#\tone\n#\n# two\n" >expect && + printf "\tone\n\ntwo\n" | git stripspace -c >actual && + test_cmp expect actual +' + test_done diff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh index 6ecb559465b43e..468a000e4bbb11 100755 --- a/t/t2024-checkout-dwim.sh +++ b/t/t2024-checkout-dwim.sh @@ -185,4 +185,22 @@ test_expect_success 'checkout -- succeeds, even if a file with the same test_branch_upstream spam repo_c spam ' +test_expect_success 'loosely defined local base branch is reported correctly' ' + + git checkout master && + git branch strict && + git branch loose && + git commit --allow-empty -m "a bit more" && + + test_config branch.strict.remote . && + test_config branch.loose.remote . && + test_config branch.strict.merge refs/heads/master && + test_config branch.loose.merge master && + + git checkout strict | sed -e "s/strict/BRANCHNAME/g" >expect && + git checkout loose | sed -e "s/loose/BRANCHNAME/g" >actual && + + test_cmp expect actual +' + test_done diff --git a/t/t2107-update-index-basic.sh b/t/t2107-update-index-basic.sh index 1bafb9098c7fae..dfe02f48183ad7 100755 --- a/t/t2107-update-index-basic.sh +++ b/t/t2107-update-index-basic.sh @@ -65,4 +65,19 @@ test_expect_success '--cacheinfo mode,sha1,path (new syntax)' ' test_cmp expect actual ' +test_expect_success '.lock files cleaned up' ' + mkdir cleanup && + ( + cd cleanup && + mkdir worktree && + git init repo && + cd repo && + git config core.worktree ../../worktree && + # --refresh triggers late setup_work_tree, + # active_cache_changed is zero, rollback_lock_file fails + git update-index --refresh && + ! test -f .git/index.lock + ) +' + test_done diff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh index 223b98433c502b..7c5ad086264e3d 100755 --- a/t/t3507-cherry-pick-conflict.sh +++ b/t/t3507-cherry-pick-conflict.sh @@ -351,19 +351,45 @@ test_expect_success 'commit after failed cherry-pick does not add duplicated -s' test_expect_success 'commit after failed cherry-pick adds -s at the right place' ' pristine_detach initial && test_must_fail git cherry-pick picked && + git commit -a -s && - pwd && - cat < expected && -picked -Signed-off-by: C O Mitter + # Do S-o-b and Conflicts appear in the right order? + cat <<-\EOF >expect && + Signed-off-by: C O Mitter + # Conflicts: + EOF + grep -e "^# Conflicts:" -e '^Signed-off-by' <.git/COMMIT_EDITMSG >actual && + test_cmp expect actual && + + cat <<-\EOF >expected && + picked -Conflicts: - foo -EOF + Signed-off-by: C O Mitter + EOF - git show -s --pretty=format:%B > actual && + git show -s --pretty=format:%B >actual && test_cmp expected actual ' +test_expect_success 'commit --amend -s places the sign-off at the right place' ' + pristine_detach initial && + test_must_fail git cherry-pick picked && + + # emulate old-style conflicts block + mv .git/MERGE_MSG .git/MERGE_MSG+ && + sed -e "/^# Conflicts:/,\$s/^# *//" <.git/MERGE_MSG+ >.git/MERGE_MSG && + + git commit -a && + git commit --amend -s && + + # Do S-o-b and Conflicts appear in the right order? + cat <<-\EOF >expect && + Signed-off-by: C O Mitter + Conflicts: + EOF + grep -e "^Conflicts:" -e '^Signed-off-by' <.git/COMMIT_EDITMSG >actual && + test_cmp expect actual +' + test_done diff --git a/t/t4008-diff-break-rewrite.sh b/t/t4008-diff-break-rewrite.sh index 27e98a8f9d6c85..89204648965b2b 100755 --- a/t/t4008-diff-break-rewrite.sh +++ b/t/t4008-diff-break-rewrite.sh @@ -123,10 +123,10 @@ test_expect_success \ 'git diff-index -B -M "$tree" >current' # file0 changed from regular to symlink. file1 is very close to the preimage of file0. -# because we break file0, file1 can become a rename of it. +# the change does not make file0 disappear, so file1 is denoted as a copy of file0 cat >expected <<\EOF :100644 120000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 67be421f88824578857624f7b3dc75e99a8a1481 T file0 -:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 R file0 file1 +:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 C file0 file1 EOF test_expect_success \ diff --git a/t/t4023-diff-rename-typechange.sh b/t/t4023-diff-rename-typechange.sh index 55d549fcf441be..8c9823765e66ac 100755 --- a/t/t4023-diff-rename-typechange.sh +++ b/t/t4023-diff-rename-typechange.sh @@ -76,7 +76,8 @@ test_expect_success 'moves and renames' ' git diff-tree three four -r --name-status -B -M | sort >actual && { - echo "R100 foo bar" + # see -B -M (#6) in t4008 + echo "C100 foo bar" echo "T100 foo" } | sort >expect && test_cmp expect actual diff --git a/t/t5700-clone-reference.sh b/t/t5700-clone-reference.sh index 6537911a430065..3e783fc450d21c 100755 --- a/t/t5700-clone-reference.sh +++ b/t/t5700-clone-reference.sh @@ -198,4 +198,21 @@ test_expect_success 'clone using repo pointed at by gitfile as reference' ' test_cmp expected "$base_dir/O/.git/objects/info/alternates" ' +test_expect_success 'clone and dissociate from reference' ' + git init P && + ( + cd P && test_commit one + ) && + git clone P Q && + ( + cd Q && test_commit two + ) && + git clone --no-local --reference=P Q R && + git clone --no-local --reference=P --dissociate Q S && + # removing the reference P would corrupt R but not S + rm -fr P && + test_must_fail git -C R fsck && + git -C S fsck +' + test_done