Merge branch 'nd/builtin-to-libgit'

Code cleanups so that libgit.a does not depend on anything in the
builtin/ directory.

* nd/builtin-to-libgit:
  fetch-pack: move core code to libgit.a
  fetch-pack: remove global (static) configuration variable "args"
  send-pack: move core code to libgit.a
  Move setup_diff_pager to libgit.a
  Move print_commit_list to libgit.a
  Move estimate_bisect_steps to libgit.a
  Move try_merge_command and checkout_fast_forward to libgit.a
This commit is contained in:
Jeff King 2012-11-09 12:51:06 -05:00
commit 19fb613695
22 changed files with 1535 additions and 1497 deletions

View File

@ -746,6 +746,7 @@ LIB_OBJS += editor.o
LIB_OBJS += entry.o
LIB_OBJS += environment.o
LIB_OBJS += exec_cmd.o
LIB_OBJS += fetch-pack.o
LIB_OBJS += fsck.o
LIB_OBJS += gettext.o
LIB_OBJS += gpg-interface.o
@ -763,6 +764,7 @@ LIB_OBJS += lockfile.o
LIB_OBJS += log-tree.o
LIB_OBJS += mailmap.o
LIB_OBJS += match-trees.o
LIB_OBJS += merge.o
LIB_OBJS += merge-file.o
LIB_OBJS += merge-recursive.o
LIB_OBJS += mergesort.o
@ -797,6 +799,7 @@ LIB_OBJS += rerere.o
LIB_OBJS += resolve-undo.o
LIB_OBJS += revision.o
LIB_OBJS += run-command.o
LIB_OBJS += send-pack.o
LIB_OBJS += sequencer.o
LIB_OBJS += server-info.o
LIB_OBJS += setup.o

View File

@ -956,3 +956,41 @@ int bisect_next_all(const char *prefix, int no_checkout)
return bisect_checkout(bisect_rev_hex, no_checkout);
}
static inline int log2i(int n)
{
int log2 = 0;
for (; n > 1; n >>= 1)
log2++;
return log2;
}
static inline int exp2i(int n)
{
return 1 << n;
}
/*
* Estimate the number of bisect steps left (after the current step)
*
* For any x between 0 included and 2^n excluded, the probability for
* n - 1 steps left looks like:
*
* P(2^n + x) == (2^n - x) / (2^n + x)
*
* and P(2^n + x) < 0.5 means 2^n < 3x
*/
int estimate_bisect_steps(int all)
{
int n, x, e;
if (all < 3)
return 0;
n = log2i(all);
e = exp2i(n);
x = all - e;
return (e < 3 * x) ? n : n - 1;
}

View File

@ -11,10 +11,6 @@ extern struct commit_list *filter_skipped(struct commit_list *list,
int *count,
int *skipped_first);
extern void print_commit_list(struct commit_list *list,
const char *format_cur,
const char *format_last);
#define BISECT_SHOW_ALL (1<<0)
#define REV_LIST_QUIET (1<<1)

View File

@ -37,10 +37,6 @@ int copy_note_for_rewrite(struct notes_rewrite_cfg *c,
const unsigned char *from_obj, const unsigned char *to_obj);
void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c);
extern int check_pager_config(const char *cmd);
struct diff_options;
extern void setup_diff_pager(struct diff_options *);
extern int textconv_object(const char *path, unsigned mode, const unsigned char *sha1, int sha1_valid, char **buf, unsigned long *buf_size);
extern int cmd_add(int argc, const char **argv, const char *prefix);

View File

@ -418,19 +418,3 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
refresh_index_quietly();
return result;
}
void setup_diff_pager(struct diff_options *opt)
{
/*
* If the user asked for our exit code, then either they want --quiet
* or --exit-code. We should definitely not bother with a pager in the
* former case, as we will generate no output. Since we still properly
* report our exit code even when a pager is run, we _could_ run a
* pager with --exit-code. But since we have not done so historically,
* and because it is easy to find people oneline advising "git diff
* --exit-code" in hooks and other scripts, we do not do so.
*/
if (!DIFF_OPT_TST(opt, EXIT_WITH_STATUS) &&
check_pager_config("diff") != 0)
setup_pager();
}

View File

@ -1,895 +1,12 @@
#include "builtin.h"
#include "refs.h"
#include "pkt-line.h"
#include "commit.h"
#include "tag.h"
#include "exec_cmd.h"
#include "pack.h"
#include "sideband.h"
#include "fetch-pack.h"
#include "remote.h"
#include "run-command.h"
#include "transport.h"
#include "version.h"
static int transfer_unpack_limit = -1;
static int fetch_unpack_limit = -1;
static int unpack_limit = 100;
static int prefer_ofs_delta = 1;
static int no_done;
static int fetch_fsck_objects = -1;
static int transfer_fsck_objects = -1;
static int agent_supported;
static struct fetch_pack_args args = {
/* .uploadpack = */ "git-upload-pack",
};
static const char fetch_pack_usage[] =
"git fetch-pack [--all] [--stdin] [--quiet|-q] [--keep|-k] [--thin] "
"[--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] "
"[--no-progress] [-v] [<host>:]<directory> [<refs>...]";
#define COMPLETE (1U << 0)
#define COMMON (1U << 1)
#define COMMON_REF (1U << 2)
#define SEEN (1U << 3)
#define POPPED (1U << 4)
static int marked;
/*
* After sending this many "have"s if we do not get any new ACK , we
* give up traversing our history.
*/
#define MAX_IN_VAIN 256
static struct commit_list *rev_list;
static int non_common_revs, multi_ack, use_sideband;
static void rev_list_push(struct commit *commit, int mark)
{
if (!(commit->object.flags & mark)) {
commit->object.flags |= mark;
if (!(commit->object.parsed))
if (parse_commit(commit))
return;
commit_list_insert_by_date(commit, &rev_list);
if (!(commit->object.flags & COMMON))
non_common_revs++;
}
}
static int rev_list_insert_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
struct object *o = deref_tag(parse_object(sha1), refname, 0);
if (o && o->type == OBJ_COMMIT)
rev_list_push((struct commit *)o, SEEN);
return 0;
}
static int clear_marks(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
struct object *o = deref_tag(parse_object(sha1), refname, 0);
if (o && o->type == OBJ_COMMIT)
clear_commit_marks((struct commit *)o,
COMMON | COMMON_REF | SEEN | POPPED);
return 0;
}
/*
This function marks a rev and its ancestors as common.
In some cases, it is desirable to mark only the ancestors (for example
when only the server does not yet know that they are common).
*/
static void mark_common(struct commit *commit,
int ancestors_only, int dont_parse)
{
if (commit != NULL && !(commit->object.flags & COMMON)) {
struct object *o = (struct object *)commit;
if (!ancestors_only)
o->flags |= COMMON;
if (!(o->flags & SEEN))
rev_list_push(commit, SEEN);
else {
struct commit_list *parents;
if (!ancestors_only && !(o->flags & POPPED))
non_common_revs--;
if (!o->parsed && !dont_parse)
if (parse_commit(commit))
return;
for (parents = commit->parents;
parents;
parents = parents->next)
mark_common(parents->item, 0, dont_parse);
}
}
}
/*
Get the next rev to send, ignoring the common.
*/
static const unsigned char *get_rev(void)
{
struct commit *commit = NULL;
while (commit == NULL) {
unsigned int mark;
struct commit_list *parents;
if (rev_list == NULL || non_common_revs == 0)
return NULL;
commit = rev_list->item;
if (!commit->object.parsed)
parse_commit(commit);
parents = commit->parents;
commit->object.flags |= POPPED;
if (!(commit->object.flags & COMMON))
non_common_revs--;
if (commit->object.flags & COMMON) {
/* do not send "have", and ignore ancestors */
commit = NULL;
mark = COMMON | SEEN;
} else if (commit->object.flags & COMMON_REF)
/* send "have", and ignore ancestors */
mark = COMMON | SEEN;
else
/* send "have", also for its ancestors */
mark = SEEN;
while (parents) {
if (!(parents->item->object.flags & SEEN))
rev_list_push(parents->item, mark);
if (mark & COMMON)
mark_common(parents->item, 1, 0);
parents = parents->next;
}
rev_list = rev_list->next;
}
return commit->object.sha1;
}
enum ack_type {
NAK = 0,
ACK,
ACK_continue,
ACK_common,
ACK_ready
};
static void consume_shallow_list(int fd)
{
if (args.stateless_rpc && args.depth > 0) {
/* If we sent a depth we will get back "duplicate"
* shallow and unshallow commands every time there
* is a block of have lines exchanged.
*/
char line[1000];
while (packet_read_line(fd, line, sizeof(line))) {
if (!prefixcmp(line, "shallow "))
continue;
if (!prefixcmp(line, "unshallow "))
continue;
die("git fetch-pack: expected shallow list");
}
}
}
struct write_shallow_data {
struct strbuf *out;
int use_pack_protocol;
int count;
};
static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
{
struct write_shallow_data *data = cb_data;
const char *hex = sha1_to_hex(graft->sha1);
data->count++;
if (data->use_pack_protocol)
packet_buf_write(data->out, "shallow %s", hex);
else {
strbuf_addstr(data->out, hex);
strbuf_addch(data->out, '\n');
}
return 0;
}
static int write_shallow_commits(struct strbuf *out, int use_pack_protocol)
{
struct write_shallow_data data;
data.out = out;
data.use_pack_protocol = use_pack_protocol;
data.count = 0;
for_each_commit_graft(write_one_shallow, &data);
return data.count;
}
static enum ack_type get_ack(int fd, unsigned char *result_sha1)
{
static char line[1000];
int len = packet_read_line(fd, line, sizeof(line));
if (!len)
die("git fetch-pack: expected ACK/NAK, got EOF");
if (line[len-1] == '\n')
line[--len] = 0;
if (!strcmp(line, "NAK"))
return NAK;
if (!prefixcmp(line, "ACK ")) {
if (!get_sha1_hex(line+4, result_sha1)) {
if (strstr(line+45, "continue"))
return ACK_continue;
if (strstr(line+45, "common"))
return ACK_common;
if (strstr(line+45, "ready"))
return ACK_ready;
return ACK;
}
}
die("git fetch_pack: expected ACK/NAK, got '%s'", line);
}
static void send_request(int fd, struct strbuf *buf)
{
if (args.stateless_rpc) {
send_sideband(fd, -1, buf->buf, buf->len, LARGE_PACKET_MAX);
packet_flush(fd);
} else
safe_write(fd, buf->buf, buf->len);
}
static void insert_one_alternate_ref(const struct ref *ref, void *unused)
{
rev_list_insert_ref(NULL, ref->old_sha1, 0, NULL);
}
#define INITIAL_FLUSH 16
#define PIPESAFE_FLUSH 32
#define LARGE_FLUSH 1024
static int next_flush(int count)
{
int flush_limit = args.stateless_rpc ? LARGE_FLUSH : PIPESAFE_FLUSH;
if (count < flush_limit)
count <<= 1;
else
count += flush_limit;
return count;
}
static int find_common(int fd[2], unsigned char *result_sha1,
struct ref *refs)
{
int fetching;
int count = 0, flushes = 0, flush_at = INITIAL_FLUSH, retval;
const unsigned char *sha1;
unsigned in_vain = 0;
int got_continue = 0;
int got_ready = 0;
struct strbuf req_buf = STRBUF_INIT;
size_t state_len = 0;
if (args.stateless_rpc && multi_ack == 1)
die("--stateless-rpc requires multi_ack_detailed");
if (marked)
for_each_ref(clear_marks, NULL);
marked = 1;
for_each_ref(rev_list_insert_ref, NULL);
for_each_alternate_ref(insert_one_alternate_ref, NULL);
fetching = 0;
for ( ; refs ; refs = refs->next) {
unsigned char *remote = refs->old_sha1;
const char *remote_hex;
struct object *o;
/*
* If that object is complete (i.e. it is an ancestor of a
* local ref), we tell them we have it but do not have to
* tell them about its ancestors, which they already know
* about.
*
* We use lookup_object here because we are only
* interested in the case we *know* the object is
* reachable and we have already scanned it.
*/
if (((o = lookup_object(remote)) != NULL) &&
(o->flags & COMPLETE)) {
continue;
}
remote_hex = sha1_to_hex(remote);
if (!fetching) {
struct strbuf c = STRBUF_INIT;
if (multi_ack == 2) strbuf_addstr(&c, " multi_ack_detailed");
if (multi_ack == 1) strbuf_addstr(&c, " multi_ack");
if (no_done) strbuf_addstr(&c, " no-done");
if (use_sideband == 2) strbuf_addstr(&c, " side-band-64k");
if (use_sideband == 1) strbuf_addstr(&c, " side-band");
if (args.use_thin_pack) strbuf_addstr(&c, " thin-pack");
if (args.no_progress) strbuf_addstr(&c, " no-progress");
if (args.include_tag) strbuf_addstr(&c, " include-tag");
if (prefer_ofs_delta) strbuf_addstr(&c, " ofs-delta");
if (agent_supported) strbuf_addf(&c, " agent=%s",
git_user_agent_sanitized());
packet_buf_write(&req_buf, "want %s%s\n", remote_hex, c.buf);
strbuf_release(&c);
} else
packet_buf_write(&req_buf, "want %s\n", remote_hex);
fetching++;
}
if (!fetching) {
strbuf_release(&req_buf);
packet_flush(fd[1]);
return 1;
}
if (is_repository_shallow())
write_shallow_commits(&req_buf, 1);
if (args.depth > 0)
packet_buf_write(&req_buf, "deepen %d", args.depth);
packet_buf_flush(&req_buf);
state_len = req_buf.len;
if (args.depth > 0) {
char line[1024];
unsigned char sha1[20];
send_request(fd[1], &req_buf);
while (packet_read_line(fd[0], line, sizeof(line))) {
if (!prefixcmp(line, "shallow ")) {
if (get_sha1_hex(line + 8, sha1))
die("invalid shallow line: %s", line);
register_shallow(sha1);
continue;
}
if (!prefixcmp(line, "unshallow ")) {
if (get_sha1_hex(line + 10, sha1))
die("invalid unshallow line: %s", line);
if (!lookup_object(sha1))
die("object not found: %s", line);
/* make sure that it is parsed as shallow */
if (!parse_object(sha1))
die("error in object: %s", line);
if (unregister_shallow(sha1))
die("no shallow found: %s", line);
continue;
}
die("expected shallow/unshallow, got %s", line);
}
} else if (!args.stateless_rpc)
send_request(fd[1], &req_buf);
if (!args.stateless_rpc) {
/* If we aren't using the stateless-rpc interface
* we don't need to retain the headers.
*/
strbuf_setlen(&req_buf, 0);
state_len = 0;
}
flushes = 0;
retval = -1;
while ((sha1 = get_rev())) {
packet_buf_write(&req_buf, "have %s\n", sha1_to_hex(sha1));
if (args.verbose)
fprintf(stderr, "have %s\n", sha1_to_hex(sha1));
in_vain++;
if (flush_at <= ++count) {
int ack;
packet_buf_flush(&req_buf);
send_request(fd[1], &req_buf);
strbuf_setlen(&req_buf, state_len);
flushes++;
flush_at = next_flush(count);
/*
* We keep one window "ahead" of the other side, and
* will wait for an ACK only on the next one
*/
if (!args.stateless_rpc && count == INITIAL_FLUSH)
continue;
consume_shallow_list(fd[0]);
do {
ack = get_ack(fd[0], result_sha1);
if (args.verbose && ack)
fprintf(stderr, "got ack %d %s\n", ack,
sha1_to_hex(result_sha1));
switch (ack) {
case ACK:
flushes = 0;
multi_ack = 0;
retval = 0;
goto done;
case ACK_common:
case ACK_ready:
case ACK_continue: {
struct commit *commit =
lookup_commit(result_sha1);
if (!commit)
die("invalid commit %s", sha1_to_hex(result_sha1));
if (args.stateless_rpc
&& ack == ACK_common
&& !(commit->object.flags & COMMON)) {
/* We need to replay the have for this object
* on the next RPC request so the peer knows
* it is in common with us.
*/
const char *hex = sha1_to_hex(result_sha1);
packet_buf_write(&req_buf, "have %s\n", hex);
state_len = req_buf.len;
}
mark_common(commit, 0, 1);
retval = 0;
in_vain = 0;
got_continue = 1;
if (ack == ACK_ready) {
rev_list = NULL;
got_ready = 1;
}
break;
}
}
} while (ack);
flushes--;
if (got_continue && MAX_IN_VAIN < in_vain) {
if (args.verbose)
fprintf(stderr, "giving up\n");
break; /* give up */
}
}
}
done:
if (!got_ready || !no_done) {
packet_buf_write(&req_buf, "done\n");
send_request(fd[1], &req_buf);
}
if (args.verbose)
fprintf(stderr, "done\n");
if (retval != 0) {
multi_ack = 0;
flushes++;
}
strbuf_release(&req_buf);
consume_shallow_list(fd[0]);
while (flushes || multi_ack) {
int ack = get_ack(fd[0], result_sha1);
if (ack) {
if (args.verbose)
fprintf(stderr, "got ack (%d) %s\n", ack,
sha1_to_hex(result_sha1));
if (ack == ACK)
return 0;
multi_ack = 1;
continue;
}
flushes--;
}
/* it is no error to fetch into a completely empty repo */
return count ? retval : 0;
}
static struct commit_list *complete;
static int mark_complete(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
struct object *o = parse_object(sha1);
while (o && o->type == OBJ_TAG) {
struct tag *t = (struct tag *) o;
if (!t->tagged)
break; /* broken repository */
o->flags |= COMPLETE;
o = parse_object(t->tagged->sha1);
}
if (o && o->type == OBJ_COMMIT) {
struct commit *commit = (struct commit *)o;
if (!(commit->object.flags & COMPLETE)) {
commit->object.flags |= COMPLETE;
commit_list_insert_by_date(commit, &complete);
}
}
return 0;
}
static void mark_recent_complete_commits(unsigned long cutoff)
{
while (complete && cutoff <= complete->item->date) {
if (args.verbose)
fprintf(stderr, "Marking %s as complete\n",
sha1_to_hex(complete->item->object.sha1));
pop_most_recent_commit(&complete, COMPLETE);
}
}
static int non_matching_ref(struct string_list_item *item, void *unused)
{
if (item->util) {
item->util = NULL;
return 0;
}
else
return 1;
}
static void filter_refs(struct ref **refs, struct string_list *sought)
{
struct ref *newlist = NULL;
struct ref **newtail = &newlist;
struct ref *ref, *next;
int sought_pos;
sought_pos = 0;
for (ref = *refs; ref; ref = next) {
int keep = 0;
next = ref->next;
if (!memcmp(ref->name, "refs/", 5) &&
check_refname_format(ref->name + 5, 0))
; /* trash */
else {
while (sought_pos < sought->nr) {
int cmp = strcmp(ref->name, sought->items[sought_pos].string);
if (cmp < 0)
break; /* definitely do not have it */
else if (cmp == 0) {
keep = 1; /* definitely have it */
sought->items[sought_pos++].util = "matched";
break;
}
else
sought_pos++; /* might have it; keep looking */
}
}
if (! keep && args.fetch_all &&
(!args.depth || prefixcmp(ref->name, "refs/tags/")))
keep = 1;
if (keep) {
*newtail = ref;
ref->next = NULL;
newtail = &ref->next;
} else {
free(ref);
}
}
filter_string_list(sought, 0, non_matching_ref, NULL);
*refs = newlist;
}
static void mark_alternate_complete(const struct ref *ref, void *unused)
{
mark_complete(NULL, ref->old_sha1, 0, NULL);
}
static int everything_local(struct ref **refs, struct string_list *sought)
{
struct ref *ref;
int retval;
unsigned long cutoff = 0;
save_commit_buffer = 0;
for (ref = *refs; ref; ref = ref->next) {
struct object *o;
o = parse_object(ref->old_sha1);
if (!o)
continue;
/* We already have it -- which may mean that we were
* in sync with the other side at some time after
* that (it is OK if we guess wrong here).
*/
if (o->type == OBJ_COMMIT) {
struct commit *commit = (struct commit *)o;
if (!cutoff || cutoff < commit->date)
cutoff = commit->date;
}
}
if (!args.depth) {
for_each_ref(mark_complete, NULL);
for_each_alternate_ref(mark_alternate_complete, NULL);
if (cutoff)
mark_recent_complete_commits(cutoff);
}
/*
* Mark all complete remote refs as common refs.
* Don't mark them common yet; the server has to be told so first.
*/
for (ref = *refs; ref; ref = ref->next) {
struct object *o = deref_tag(lookup_object(ref->old_sha1),
NULL, 0);
if (!o || o->type != OBJ_COMMIT || !(o->flags & COMPLETE))
continue;
if (!(o->flags & SEEN)) {
rev_list_push((struct commit *)o, COMMON_REF | SEEN);
mark_common((struct commit *)o, 1, 1);
}
}
filter_refs(refs, sought);
for (retval = 1, ref = *refs; ref ; ref = ref->next) {
const unsigned char *remote = ref->old_sha1;
unsigned char local[20];
struct object *o;
o = lookup_object(remote);
if (!o || !(o->flags & COMPLETE)) {
retval = 0;
if (!args.verbose)
continue;
fprintf(stderr,
"want %s (%s)\n", sha1_to_hex(remote),
ref->name);
continue;
}
hashcpy(ref->new_sha1, local);
if (!args.verbose)
continue;
fprintf(stderr,
"already have %s (%s)\n", sha1_to_hex(remote),
ref->name);
}
return retval;
}
static int sideband_demux(int in, int out, void *data)
{
int *xd = data;
int ret = recv_sideband("fetch-pack", xd[0], out);
close(out);
return ret;
}
static int get_pack(int xd[2], char **pack_lockfile)
{
struct async demux;
const char *argv[20];
char keep_arg[256];
char hdr_arg[256];
const char **av;
int do_keep = args.keep_pack;
struct child_process cmd;
memset(&demux, 0, sizeof(demux));
if (use_sideband) {
/* xd[] is talking with upload-pack; subprocess reads from
* xd[0], spits out band#2 to stderr, and feeds us band#1
* through demux->out.
*/
demux.proc = sideband_demux;
demux.data = xd;
demux.out = -1;
if (start_async(&demux))
die("fetch-pack: unable to fork off sideband"
" demultiplexer");
}
else
demux.out = xd[0];
memset(&cmd, 0, sizeof(cmd));
cmd.argv = argv;
av = argv;
*hdr_arg = 0;
if (!args.keep_pack && unpack_limit) {
struct pack_header header;
if (read_pack_header(demux.out, &header))
die("protocol error: bad pack header");
snprintf(hdr_arg, sizeof(hdr_arg),
"--pack_header=%"PRIu32",%"PRIu32,
ntohl(header.hdr_version), ntohl(header.hdr_entries));
if (ntohl(header.hdr_entries) < unpack_limit)
do_keep = 0;
else
do_keep = 1;
}
if (do_keep) {
if (pack_lockfile)
cmd.out = -1;
*av++ = "index-pack";
*av++ = "--stdin";
if (!args.quiet && !args.no_progress)
*av++ = "-v";
if (args.use_thin_pack)
*av++ = "--fix-thin";
if (args.lock_pack || unpack_limit) {
int s = sprintf(keep_arg,
"--keep=fetch-pack %"PRIuMAX " on ", (uintmax_t) getpid());
if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
strcpy(keep_arg + s, "localhost");
*av++ = keep_arg;
}
}
else {
*av++ = "unpack-objects";
if (args.quiet || args.no_progress)
*av++ = "-q";
}
if (*hdr_arg)
*av++ = hdr_arg;
if (fetch_fsck_objects >= 0
? fetch_fsck_objects
: transfer_fsck_objects >= 0
? transfer_fsck_objects
: 0)
*av++ = "--strict";
*av++ = NULL;
cmd.in = demux.out;
cmd.git_cmd = 1;
if (start_command(&cmd))
die("fetch-pack: unable to fork off %s", argv[0]);
if (do_keep && pack_lockfile) {
*pack_lockfile = index_pack_lockfile(cmd.out);
close(cmd.out);
}
if (finish_command(&cmd))
die("%s failed", argv[0]);
if (use_sideband && finish_async(&demux))
die("error in sideband demultiplexer");
return 0;
}
static struct ref *do_fetch_pack(int fd[2],
const struct ref *orig_ref,
struct string_list *sought,
char **pack_lockfile)
{
struct ref *ref = copy_ref_list(orig_ref);
unsigned char sha1[20];
const char *agent_feature;
int agent_len;
sort_ref_list(&ref, ref_compare_name);
if (is_repository_shallow() && !server_supports("shallow"))
die("Server does not support shallow clients");
if (server_supports("multi_ack_detailed")) {
if (args.verbose)
fprintf(stderr, "Server supports multi_ack_detailed\n");
multi_ack = 2;
if (server_supports("no-done")) {
if (args.verbose)
fprintf(stderr, "Server supports no-done\n");
if (args.stateless_rpc)
no_done = 1;
}
}
else if (server_supports("multi_ack")) {
if (args.verbose)
fprintf(stderr, "Server supports multi_ack\n");
multi_ack = 1;
}
if (server_supports("side-band-64k")) {
if (args.verbose)
fprintf(stderr, "Server supports side-band-64k\n");
use_sideband = 2;
}
else if (server_supports("side-band")) {
if (args.verbose)
fprintf(stderr, "Server supports side-band\n");
use_sideband = 1;
}
if (!server_supports("thin-pack"))
args.use_thin_pack = 0;
if (!server_supports("no-progress"))
args.no_progress = 0;
if (!server_supports("include-tag"))
args.include_tag = 0;
if (server_supports("ofs-delta")) {
if (args.verbose)
fprintf(stderr, "Server supports ofs-delta\n");
} else
prefer_ofs_delta = 0;
if ((agent_feature = server_feature_value("agent", &agent_len))) {
agent_supported = 1;
if (args.verbose && agent_len)
fprintf(stderr, "Server version is %.*s\n",
agent_len, agent_feature);
}
if (everything_local(&ref, sought)) {
packet_flush(fd[1]);
goto all_done;
}
if (find_common(fd, sha1, ref) < 0)
if (!args.keep_pack)
/* When cloning, it is not unusual to have
* no common commit.
*/
warning("no common commits");
if (args.stateless_rpc)
packet_flush(fd[1]);
if (get_pack(fd, pack_lockfile))
die("git fetch-pack: fetch failed.");
all_done:
return ref;
}
static int fetch_pack_config(const char *var, const char *value, void *cb)
{
if (strcmp(var, "fetch.unpacklimit") == 0) {
fetch_unpack_limit = git_config_int(var, value);
return 0;
}
if (strcmp(var, "transfer.unpacklimit") == 0) {
transfer_unpack_limit = git_config_int(var, value);
return 0;
}
if (strcmp(var, "repack.usedeltabaseoffset") == 0) {
prefer_ofs_delta = git_config_bool(var, value);
return 0;
}
if (!strcmp(var, "fetch.fsckobjects")) {
fetch_fsck_objects = git_config_bool(var, value);
return 0;
}
if (!strcmp(var, "transfer.fsckobjects")) {
transfer_fsck_objects = git_config_bool(var, value);
return 0;
}
return git_default_config(var, value, cb);
}
static struct lock_file lock;
static void fetch_pack_setup(void)
{
static int did_setup;
if (did_setup)
return;
git_config(fetch_pack_config, NULL);
if (0 <= transfer_unpack_limit)
unpack_limit = transfer_unpack_limit;
else if (0 <= fetch_unpack_limit)
unpack_limit = fetch_unpack_limit;
did_setup = 1;
}
int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
{
int i, ret;
@ -900,9 +17,13 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
char *pack_lockfile = NULL;
char **pack_lockfile_ptr = NULL;
struct child_process *conn;
struct fetch_pack_args args;
packet_trace_identity("fetch-pack");
memset(&args, 0, sizeof(args));
args.uploadpack = "git-upload-pack";
for (i = 1; i < argc && *argv[i] == '-'; i++) {
const char *arg = argv[i];
@ -1038,66 +159,3 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
return ret;
}
struct ref *fetch_pack(struct fetch_pack_args *my_args,
int fd[], struct child_process *conn,
const struct ref *ref,
const char *dest,
struct string_list *sought,
char **pack_lockfile)
{
struct stat st;
struct ref *ref_cpy;
fetch_pack_setup();
if (&args != my_args)
memcpy(&args, my_args, sizeof(args));
if (args.depth > 0) {
if (stat(git_path("shallow"), &st))
st.st_mtime = 0;
}
if (sought->nr) {
sort_string_list(sought);
string_list_remove_duplicates(sought, 0);
}
if (!ref) {
packet_flush(fd[1]);
die("no matching remote head");
}
ref_cpy = do_fetch_pack(fd, ref, sought, pack_lockfile);
if (args.depth > 0) {
struct cache_time mtime;
struct strbuf sb = STRBUF_INIT;
char *shallow = git_path("shallow");
int fd;
mtime.sec = st.st_mtime;
mtime.nsec = ST_MTIME_NSEC(st);
if (stat(shallow, &st)) {
if (mtime.sec)
die("shallow file was removed during fetch");
} else if (st.st_mtime != mtime.sec
#ifdef USE_NSEC
|| ST_MTIME_NSEC(st) != mtime.nsec
#endif
)
die("shallow file was changed during fetch");
fd = hold_lock_file_for_update(&lock, shallow,
LOCK_DIE_ON_ERROR);
if (!write_shallow_commits(&sb, 0)
|| write_in_full(fd, sb.buf, sb.len) != sb.len) {
unlink_or_warn(shallow);
rollback_lock_file(&lock);
} else {
commit_lock_file(&lock);
}
strbuf_release(&sb);
}
reprepare_packed_git();
return ref_cpy;
}

View File

@ -628,59 +628,6 @@ static void write_tree_trivial(unsigned char *sha1)
die(_("git write-tree failed to write a tree"));
}
static const char *merge_argument(struct commit *commit)
{
if (commit)
return sha1_to_hex(commit->object.sha1);
else
return EMPTY_TREE_SHA1_HEX;
}
int try_merge_command(const char *strategy, size_t xopts_nr,
const char **xopts, struct commit_list *common,
const char *head_arg, struct commit_list *remotes)
{
const char **args;
int i = 0, x = 0, ret;
struct commit_list *j;
struct strbuf buf = STRBUF_INIT;
args = xmalloc((4 + xopts_nr + commit_list_count(common) +
commit_list_count(remotes)) * sizeof(char *));
strbuf_addf(&buf, "merge-%s", strategy);
args[i++] = buf.buf;
for (x = 0; x < xopts_nr; x++) {
char *s = xmalloc(strlen(xopts[x])+2+1);
strcpy(s, "--");
strcpy(s+2, xopts[x]);
args[i++] = s;
}
for (j = common; j; j = j->next)
args[i++] = xstrdup(merge_argument(j->item));
args[i++] = "--";
args[i++] = head_arg;
for (j = remotes; j; j = j->next)
args[i++] = xstrdup(merge_argument(j->item));
args[i] = NULL;
ret = run_command_v_opt(args, RUN_GIT_CMD);
strbuf_release(&buf);
i = 1;
for (x = 0; x < xopts_nr; x++)
free((void *)args[i++]);
for (j = common; j; j = j->next)
free((void *)args[i++]);
i += 2;
for (j = remotes; j; j = j->next)
free((void *)args[i++]);
free(args);
discard_cache();
if (read_cache() < 0)
die(_("failed to read the cache"));
resolve_undo_clear();
return ret;
}
static int try_merge_strategy(const char *strategy, struct commit_list *common,
struct commit_list *remoteheads,
struct commit *head, const char *head_arg)
@ -762,56 +709,6 @@ static int count_unmerged_entries(void)
return ret;
}
int checkout_fast_forward(const unsigned char *head, const unsigned char *remote)
{
struct tree *trees[MAX_UNPACK_TREES];
struct unpack_trees_options opts;
struct tree_desc t[MAX_UNPACK_TREES];
int i, fd, nr_trees = 0;
struct dir_struct dir;
struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
refresh_cache(REFRESH_QUIET);
fd = hold_locked_index(lock_file, 1);
memset(&trees, 0, sizeof(trees));
memset(&opts, 0, sizeof(opts));
memset(&t, 0, sizeof(t));
if (overwrite_ignore) {
memset(&dir, 0, sizeof(dir));
dir.flags |= DIR_SHOW_IGNORED;
setup_standard_excludes(&dir);
opts.dir = &dir;
}
opts.head_idx = 1;
opts.src_index = &the_index;
opts.dst_index = &the_index;
opts.update = 1;
opts.verbose_update = 1;
opts.merge = 1;
opts.fn = twoway_merge;
setup_unpack_trees_porcelain(&opts, "merge");
trees[nr_trees] = parse_tree_indirect(head);
if (!trees[nr_trees++])
return -1;
trees[nr_trees] = parse_tree_indirect(remote);
if (!trees[nr_trees++])
return -1;
for (i = 0; i < nr_trees; i++) {
parse_tree(trees[i]);
init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
}
if (unpack_trees(nr_trees, t, &opts))
return -1;
if (write_cache(fd, active_cache, active_nr) ||
commit_locked_index(lock_file))
die(_("unable to write new index file"));
return 0;
}
static void split_merge_strategies(const char *string, struct strategy **list,
int *nr, int *alloc)
{
@ -1424,7 +1321,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
}
if (checkout_fast_forward(head_commit->object.sha1,
commit->object.sha1)) {
commit->object.sha1,
overwrite_ignore)) {
ret = 1;
goto done;
}

View File

@ -201,55 +201,6 @@ static void show_edge(struct commit *commit)
printf("-%s\n", sha1_to_hex(commit->object.sha1));
}
static inline int log2i(int n)
{
int log2 = 0;
for (; n > 1; n >>= 1)
log2++;
return log2;
}
static inline int exp2i(int n)
{
return 1 << n;
}
/*
* Estimate the number of bisect steps left (after the current step)
*
* For any x between 0 included and 2^n excluded, the probability for
* n - 1 steps left looks like:
*
* P(2^n + x) == (2^n - x) / (2^n + x)
*
* and P(2^n + x) < 0.5 means 2^n < 3x
*/
int estimate_bisect_steps(int all)
{
int n, x, e;
if (all < 3)
return 0;
n = log2i(all);
e = exp2i(n);
x = all - e;
return (e < 3 * x) ? n : n - 1;
}
void print_commit_list(struct commit_list *list,
const char *format_cur,
const char *format_last)
{
for ( ; list; list = list->next) {
const char *format = list->next ? format_cur : format_last;
printf(format, sha1_to_hex(list->item->object.sha1));
}
}
static void print_var_str(const char *var, const char *val)
{
printf("%s='%s'\n", var, val);

View File

@ -16,164 +16,6 @@ static const char send_pack_usage[] =
static struct send_pack_args args;
static int feed_object(const unsigned char *sha1, int fd, int negative)
{
char buf[42];
if (negative && !has_sha1_file(sha1))
return 1;
memcpy(buf + negative, sha1_to_hex(sha1), 40);
if (negative)
buf[0] = '^';
buf[40 + negative] = '\n';
return write_or_whine(fd, buf, 41 + negative, "send-pack: send refs");
}
/*
* Make a pack stream and spit it out into file descriptor fd
*/
static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *extra, struct send_pack_args *args)
{
/*
* The child becomes pack-objects --revs; we feed
* the revision parameters to it via its stdin and
* let its stdout go back to the other end.
*/
const char *argv[] = {
"pack-objects",
"--all-progress-implied",
"--revs",
"--stdout",
NULL,
NULL,
NULL,
NULL,
NULL,
};
struct child_process po;
int i;
i = 4;
if (args->use_thin_pack)
argv[i++] = "--thin";
if (args->use_ofs_delta)
argv[i++] = "--delta-base-offset";
if (args->quiet || !args->progress)
argv[i++] = "-q";
if (args->progress)
argv[i++] = "--progress";
memset(&po, 0, sizeof(po));
po.argv = argv;
po.in = -1;
po.out = args->stateless_rpc ? -1 : fd;
po.git_cmd = 1;
if (start_command(&po))
die_errno("git pack-objects failed");
/*
* We feed the pack-objects we just spawned with revision
* parameters by writing to the pipe.
*/
for (i = 0; i < extra->nr; i++)
if (!feed_object(extra->array[i], po.in, 1))
break;
while (refs) {
if (!is_null_sha1(refs->old_sha1) &&
!feed_object(refs->old_sha1, po.in, 1))
break;
if (!is_null_sha1(refs->new_sha1) &&
!feed_object(refs->new_sha1, po.in, 0))
break;
refs = refs->next;
}
close(po.in);
if (args->stateless_rpc) {
char *buf = xmalloc(LARGE_PACKET_MAX);
while (1) {
ssize_t n = xread(po.out, buf, LARGE_PACKET_MAX);
if (n <= 0)
break;
send_sideband(fd, -1, buf, n, LARGE_PACKET_MAX);
}
free(buf);
close(po.out);
po.out = -1;
}
if (finish_command(&po))
return -1;
return 0;
}
static int receive_status(int in, struct ref *refs)
{
struct ref *hint;
char line[1000];
int ret = 0;
int len = packet_read_line(in, line, sizeof(line));
if (len < 10 || memcmp(line, "unpack ", 7))
return error("did not receive remote status");
if (memcmp(line, "unpack ok\n", 10)) {
char *p = line + strlen(line) - 1;
if (*p == '\n')
*p = '\0';
error("unpack failed: %s", line + 7);
ret = -1;
}
hint = NULL;
while (1) {
char *refname;
char *msg;
len = packet_read_line(in, line, sizeof(line));
if (!len)
break;
if (len < 3 ||
(memcmp(line, "ok ", 3) && memcmp(line, "ng ", 3))) {
fprintf(stderr, "protocol error: %s\n", line);
ret = -1;
break;
}
line[strlen(line)-1] = '\0';
refname = line + 3;
msg = strchr(refname, ' ');
if (msg)
*msg++ = '\0';
/* first try searching at our hint, falling back to all refs */
if (hint)
hint = find_ref_by_name(hint, refname);
if (!hint)
hint = find_ref_by_name(refs, refname);
if (!hint) {
warning("remote reported status on unknown ref: %s",
refname);
continue;
}
if (hint->status != REF_STATUS_EXPECTING_REPORT) {
warning("remote reported status on unexpected ref: %s",
refname);
continue;
}
if (line[0] == 'o' && line[1] == 'k')
hint->status = REF_STATUS_OK;
else {
hint->status = REF_STATUS_REMOTE_REJECT;
ret = -1;
}
if (msg)
hint->remote_status = xstrdup(msg);
/* start our next search from the next ref */
hint = hint->next;
}
return ret;
}
static void print_helper_status(struct ref *ref)
{
struct strbuf buf = STRBUF_INIT;
@ -227,181 +69,6 @@ static void print_helper_status(struct ref *ref)
strbuf_release(&buf);
}
static int sideband_demux(int in, int out, void *data)
{
int *fd = data, ret;
#ifdef NO_PTHREADS
close(fd[1]);
#endif
ret = recv_sideband("send-pack", fd[0], out);
close(out);
return ret;
}
int send_pack(struct send_pack_args *args,
int fd[], struct child_process *conn,
struct ref *remote_refs,
struct extra_have_objects *extra_have)
{
int in = fd[0];
int out = fd[1];
struct strbuf req_buf = STRBUF_INIT;
struct ref *ref;
int new_refs;
int allow_deleting_refs = 0;
int status_report = 0;
int use_sideband = 0;
int quiet_supported = 0;
int agent_supported = 0;
unsigned cmds_sent = 0;
int ret;
struct async demux;
/* Does the other end support the reporting? */
if (server_supports("report-status"))
status_report = 1;
if (server_supports("delete-refs"))
allow_deleting_refs = 1;
if (server_supports("ofs-delta"))
args->use_ofs_delta = 1;
if (server_supports("side-band-64k"))
use_sideband = 1;
if (server_supports("quiet"))
quiet_supported = 1;
if (server_supports("agent"))
agent_supported = 1;
if (!remote_refs) {
fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
"Perhaps you should specify a branch such as 'master'.\n");
return 0;
}
/*
* Finally, tell the other end!
*/
new_refs = 0;
for (ref = remote_refs; ref; ref = ref->next) {
if (!ref->peer_ref && !args->send_mirror)
continue;
/* Check for statuses set by set_ref_status_for_push() */
switch (ref->status) {
case REF_STATUS_REJECT_NONFASTFORWARD:
case REF_STATUS_UPTODATE:
continue;
default:
; /* do nothing */
}
if (ref->deletion && !allow_deleting_refs) {
ref->status = REF_STATUS_REJECT_NODELETE;
continue;
}
if (!ref->deletion)
new_refs++;
if (args->dry_run) {
ref->status = REF_STATUS_OK;
} else {
char *old_hex = sha1_to_hex(ref->old_sha1);
char *new_hex = sha1_to_hex(ref->new_sha1);
int quiet = quiet_supported && (args->quiet || !args->progress);
if (!cmds_sent && (status_report || use_sideband ||
quiet || agent_supported)) {
packet_buf_write(&req_buf,
"%s %s %s%c%s%s%s%s%s",
old_hex, new_hex, ref->name, 0,
status_report ? " report-status" : "",
use_sideband ? " side-band-64k" : "",
quiet ? " quiet" : "",
agent_supported ? " agent=" : "",
agent_supported ? git_user_agent_sanitized() : ""
);
}
else
packet_buf_write(&req_buf, "%s %s %s",
old_hex, new_hex, ref->name);
ref->status = status_report ?
REF_STATUS_EXPECTING_REPORT :
REF_STATUS_OK;
cmds_sent++;
}
}
if (args->stateless_rpc) {
if (!args->dry_run && cmds_sent) {
packet_buf_flush(&req_buf);
send_sideband(out, -1, req_buf.buf, req_buf.len, LARGE_PACKET_MAX);
}
} else {
safe_write(out, req_buf.buf, req_buf.len);
packet_flush(out);
}
strbuf_release(&req_buf);
if (use_sideband && cmds_sent) {
memset(&demux, 0, sizeof(demux));
demux.proc = sideband_demux;
demux.data = fd;
demux.out = -1;
if (start_async(&demux))
die("send-pack: unable to fork off sideband demultiplexer");
in = demux.out;
}
if (new_refs && cmds_sent) {
if (pack_objects(out, remote_refs, extra_have, args) < 0) {
for (ref = remote_refs; ref; ref = ref->next)
ref->status = REF_STATUS_NONE;
if (args->stateless_rpc)
close(out);
if (git_connection_is_socket(conn))
shutdown(fd[0], SHUT_WR);
if (use_sideband)
finish_async(&demux);
return -1;
}
}
if (args->stateless_rpc && cmds_sent)
packet_flush(out);
if (status_report && cmds_sent)
ret = receive_status(in, remote_refs);
else
ret = 0;
if (args->stateless_rpc)
packet_flush(out);
if (use_sideband && cmds_sent) {
if (finish_async(&demux)) {
error("error in sideband demultiplexer");
ret = -1;
}
close(demux.out);
}
if (ret < 0)
return ret;
if (args->porcelain)
return 0;
for (ref = remote_refs; ref; ref = ref->next) {
switch (ref->status) {
case REF_STATUS_NONE:
case REF_STATUS_UPTODATE:
case REF_STATUS_OK:
break;
default:
return -1;
}
}
return 0;
}
int cmd_send_pack(int argc, const char **argv, const char *prefix)
{
int i, nr_refspecs = 0;

12
cache.h
View File

@ -1183,6 +1183,7 @@ extern int pager_in_use(void);
extern int pager_use_color;
extern int term_columns(void);
extern int decimal_width(int);
extern int check_pager_config(const char *cmd);
extern const char *editor_program;
extern const char *askpass_program;
@ -1265,8 +1266,15 @@ struct startup_info {
};
extern struct startup_info *startup_info;
/* builtin/merge.c */
int checkout_fast_forward(const unsigned char *from, const unsigned char *to);
/* merge.c */
struct commit_list;
int try_merge_command(const char *strategy, size_t xopts_nr,
const char **xopts, struct commit_list *common,
const char *head_arg, struct commit_list *remotes);
int checkout_fast_forward(const unsigned char *from,
const unsigned char *to,
int overwrite_ignore);
int sane_execvp(const char *file, char *const argv[]);

View File

@ -1347,3 +1347,13 @@ struct commit_list **commit_list_append(struct commit *commit,
new->next = NULL;
return &new->next;
}
void print_commit_list(struct commit_list *list,
const char *format_cur,
const char *format_last)
{
for ( ; list; list = list->next) {
const char *format = list->next ? format_cur : format_last;
printf(format, sha1_to_hex(list->item->object.sha1));
}
}

View File

@ -222,4 +222,8 @@ struct commit *get_merge_parent(const char *name);
extern int parse_signed_commit(const unsigned char *sha1,
struct strbuf *message, struct strbuf *signature);
extern void print_commit_list(struct commit_list *list,
const char *format_cur,
const char *format_last);
#endif /* COMMIT_H */

16
diff.c
View File

@ -4880,3 +4880,19 @@ size_t fill_textconv(struct userdiff_driver *driver,
return size;
}
void setup_diff_pager(struct diff_options *opt)
{
/*
* If the user asked for our exit code, then either they want --quiet
* or --exit-code. We should definitely not bother with a pager in the
* former case, as we will generate no output. Since we still properly
* report our exit code even when a pager is run, we _could_ run a
* pager with --exit-code. But since we have not done so historically,
* and because it is easy to find people oneline advising "git diff
* --exit-code" in hooks and other scripts, we do not do so.
*/
if (!DIFF_OPT_TST(opt, EXIT_WITH_STATUS) &&
check_pager_config("diff") != 0)
setup_pager();
}

1
diff.h
View File

@ -335,5 +335,6 @@ extern int parse_rename_score(const char **cp_p);
extern int print_stat_summary(FILE *fp, int files,
int insertions, int deletions);
extern void setup_diff_pager(struct diff_options *);
#endif /* DIFF_H */

951
fetch-pack.c Normal file
View File

@ -0,0 +1,951 @@
#include "cache.h"
#include "refs.h"
#include "pkt-line.h"
#include "commit.h"
#include "tag.h"
#include "exec_cmd.h"
#include "pack.h"
#include "sideband.h"
#include "fetch-pack.h"
#include "remote.h"
#include "run-command.h"
#include "transport.h"
#include "version.h"
static int transfer_unpack_limit = -1;
static int fetch_unpack_limit = -1;
static int unpack_limit = 100;
static int prefer_ofs_delta = 1;
static int no_done;
static int fetch_fsck_objects = -1;
static int transfer_fsck_objects = -1;
static int agent_supported;
#define COMPLETE (1U << 0)
#define COMMON (1U << 1)
#define COMMON_REF (1U << 2)
#define SEEN (1U << 3)
#define POPPED (1U << 4)
static int marked;
/*
* After sending this many "have"s if we do not get any new ACK , we
* give up traversing our history.
*/
#define MAX_IN_VAIN 256
static struct commit_list *rev_list;
static int non_common_revs, multi_ack, use_sideband;
static void rev_list_push(struct commit *commit, int mark)
{
if (!(commit->object.flags & mark)) {
commit->object.flags |= mark;
if (!(commit->object.parsed))
if (parse_commit(commit))
return;
commit_list_insert_by_date(commit, &rev_list);
if (!(commit->object.flags & COMMON))
non_common_revs++;
}
}
static int rev_list_insert_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
struct object *o = deref_tag(parse_object(sha1), refname, 0);
if (o && o->type == OBJ_COMMIT)
rev_list_push((struct commit *)o, SEEN);
return 0;
}
static int clear_marks(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
struct object *o = deref_tag(parse_object(sha1), refname, 0);
if (o && o->type == OBJ_COMMIT)
clear_commit_marks((struct commit *)o,
COMMON | COMMON_REF | SEEN | POPPED);
return 0;
}
/*
This function marks a rev and its ancestors as common.
In some cases, it is desirable to mark only the ancestors (for example
when only the server does not yet know that they are common).
*/
static void mark_common(struct commit *commit,
int ancestors_only, int dont_parse)
{
if (commit != NULL && !(commit->object.flags & COMMON)) {
struct object *o = (struct object *)commit;
if (!ancestors_only)
o->flags |= COMMON;
if (!(o->flags & SEEN))
rev_list_push(commit, SEEN);
else {
struct commit_list *parents;
if (!ancestors_only && !(o->flags & POPPED))
non_common_revs--;
if (!o->parsed && !dont_parse)
if (parse_commit(commit))
return;
for (parents = commit->parents;
parents;
parents = parents->next)
mark_common(parents->item, 0, dont_parse);
}
}
}
/*
Get the next rev to send, ignoring the common.
*/
static const unsigned char *get_rev(void)
{
struct commit *commit = NULL;
while (commit == NULL) {
unsigned int mark;
struct commit_list *parents;
if (rev_list == NULL || non_common_revs == 0)
return NULL;
commit = rev_list->item;
if (!commit->object.parsed)
parse_commit(commit);
parents = commit->parents;
commit->object.flags |= POPPED;
if (!(commit->object.flags & COMMON))
non_common_revs--;
if (commit->object.flags & COMMON) {
/* do not send "have", and ignore ancestors */
commit = NULL;
mark = COMMON | SEEN;
} else if (commit->object.flags & COMMON_REF)
/* send "have", and ignore ancestors */
mark = COMMON | SEEN;
else
/* send "have", also for its ancestors */
mark = SEEN;
while (parents) {
if (!(parents->item->object.flags & SEEN))
rev_list_push(parents->item, mark);
if (mark & COMMON)
mark_common(parents->item, 1, 0);
parents = parents->next;
}
rev_list = rev_list->next;
}
return commit->object.sha1;
}
enum ack_type {
NAK = 0,
ACK,
ACK_continue,
ACK_common,
ACK_ready
};
static void consume_shallow_list(struct fetch_pack_args *args, int fd)
{
if (args->stateless_rpc && args->depth > 0) {
/* If we sent a depth we will get back "duplicate"
* shallow and unshallow commands every time there
* is a block of have lines exchanged.
*/
char line[1000];
while (packet_read_line(fd, line, sizeof(line))) {
if (!prefixcmp(line, "shallow "))
continue;
if (!prefixcmp(line, "unshallow "))
continue;
die("git fetch-pack: expected shallow list");
}
}
}
struct write_shallow_data {
struct strbuf *out;
int use_pack_protocol;
int count;
};
static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
{
struct write_shallow_data *data = cb_data;
const char *hex = sha1_to_hex(graft->sha1);
data->count++;
if (data->use_pack_protocol)
packet_buf_write(data->out, "shallow %s", hex);
else {
strbuf_addstr(data->out, hex);
strbuf_addch(data->out, '\n');
}
return 0;
}
static int write_shallow_commits(struct strbuf *out, int use_pack_protocol)
{
struct write_shallow_data data;
data.out = out;
data.use_pack_protocol = use_pack_protocol;
data.count = 0;
for_each_commit_graft(write_one_shallow, &data);
return data.count;
}
static enum ack_type get_ack(int fd, unsigned char *result_sha1)
{
static char line[1000];
int len = packet_read_line(fd, line, sizeof(line));
if (!len)
die("git fetch-pack: expected ACK/NAK, got EOF");
if (line[len-1] == '\n')
line[--len] = 0;
if (!strcmp(line, "NAK"))
return NAK;
if (!prefixcmp(line, "ACK ")) {
if (!get_sha1_hex(line+4, result_sha1)) {
if (strstr(line+45, "continue"))
return ACK_continue;
if (strstr(line+45, "common"))
return ACK_common;
if (strstr(line+45, "ready"))
return ACK_ready;
return ACK;
}
}
die("git fetch_pack: expected ACK/NAK, got '%s'", line);
}
static void send_request(struct fetch_pack_args *args,
int fd, struct strbuf *buf)
{
if (args->stateless_rpc) {
send_sideband(fd, -1, buf->buf, buf->len, LARGE_PACKET_MAX);
packet_flush(fd);
} else
safe_write(fd, buf->buf, buf->len);
}
static void insert_one_alternate_ref(const struct ref *ref, void *unused)
{
rev_list_insert_ref(NULL, ref->old_sha1, 0, NULL);
}
#define INITIAL_FLUSH 16
#define PIPESAFE_FLUSH 32
#define LARGE_FLUSH 1024
static int next_flush(struct fetch_pack_args *args, int count)
{
int flush_limit = args->stateless_rpc ? LARGE_FLUSH : PIPESAFE_FLUSH;
if (count < flush_limit)
count <<= 1;
else
count += flush_limit;
return count;
}
static int find_common(struct fetch_pack_args *args,
int fd[2], unsigned char *result_sha1,
struct ref *refs)
{
int fetching;
int count = 0, flushes = 0, flush_at = INITIAL_FLUSH, retval;
const unsigned char *sha1;
unsigned in_vain = 0;
int got_continue = 0;
int got_ready = 0;
struct strbuf req_buf = STRBUF_INIT;
size_t state_len = 0;
if (args->stateless_rpc && multi_ack == 1)
die("--stateless-rpc requires multi_ack_detailed");
if (marked)
for_each_ref(clear_marks, NULL);
marked = 1;
for_each_ref(rev_list_insert_ref, NULL);
for_each_alternate_ref(insert_one_alternate_ref, NULL);
fetching = 0;
for ( ; refs ; refs = refs->next) {
unsigned char *remote = refs->old_sha1;
const char *remote_hex;
struct object *o;
/*
* If that object is complete (i.e. it is an ancestor of a
* local ref), we tell them we have it but do not have to
* tell them about its ancestors, which they already know
* about.
*
* We use lookup_object here because we are only
* interested in the case we *know* the object is
* reachable and we have already scanned it.
*/
if (((o = lookup_object(remote)) != NULL) &&
(o->flags & COMPLETE)) {
continue;
}
remote_hex = sha1_to_hex(remote);
if (!fetching) {
struct strbuf c = STRBUF_INIT;
if (multi_ack == 2) strbuf_addstr(&c, " multi_ack_detailed");
if (multi_ack == 1) strbuf_addstr(&c, " multi_ack");
if (no_done) strbuf_addstr(&c, " no-done");
if (use_sideband == 2) strbuf_addstr(&c, " side-band-64k");
if (use_sideband == 1) strbuf_addstr(&c, " side-band");
if (args->use_thin_pack) strbuf_addstr(&c, " thin-pack");
if (args->no_progress) strbuf_addstr(&c, " no-progress");
if (args->include_tag) strbuf_addstr(&c, " include-tag");
if (prefer_ofs_delta) strbuf_addstr(&c, " ofs-delta");
if (agent_supported) strbuf_addf(&c, " agent=%s",
git_user_agent_sanitized());
packet_buf_write(&req_buf, "want %s%s\n", remote_hex, c.buf);
strbuf_release(&c);
} else
packet_buf_write(&req_buf, "want %s\n", remote_hex);
fetching++;
}
if (!fetching) {
strbuf_release(&req_buf);
packet_flush(fd[1]);
return 1;
}
if (is_repository_shallow())
write_shallow_commits(&req_buf, 1);
if (args->depth > 0)
packet_buf_write(&req_buf, "deepen %d", args->depth);
packet_buf_flush(&req_buf);
state_len = req_buf.len;
if (args->depth > 0) {
char line[1024];
unsigned char sha1[20];
send_request(args, fd[1], &req_buf);
while (packet_read_line(fd[0], line, sizeof(line))) {
if (!prefixcmp(line, "shallow ")) {
if (get_sha1_hex(line + 8, sha1))
die("invalid shallow line: %s", line);
register_shallow(sha1);
continue;
}
if (!prefixcmp(line, "unshallow ")) {
if (get_sha1_hex(line + 10, sha1))
die("invalid unshallow line: %s", line);
if (!lookup_object(sha1))
die("object not found: %s", line);
/* make sure that it is parsed as shallow */
if (!parse_object(sha1))
die("error in object: %s", line);
if (unregister_shallow(sha1))
die("no shallow found: %s", line);
continue;
}
die("expected shallow/unshallow, got %s", line);
}
} else if (!args->stateless_rpc)
send_request(args, fd[1], &req_buf);
if (!args->stateless_rpc) {
/* If we aren't using the stateless-rpc interface
* we don't need to retain the headers.
*/
strbuf_setlen(&req_buf, 0);
state_len = 0;
}
flushes = 0;
retval = -1;
while ((sha1 = get_rev())) {
packet_buf_write(&req_buf, "have %s\n", sha1_to_hex(sha1));
if (args->verbose)
fprintf(stderr, "have %s\n", sha1_to_hex(sha1));
in_vain++;
if (flush_at <= ++count) {
int ack;
packet_buf_flush(&req_buf);
send_request(args, fd[1], &req_buf);
strbuf_setlen(&req_buf, state_len);
flushes++;
flush_at = next_flush(args, count);
/*
* We keep one window "ahead" of the other side, and
* will wait for an ACK only on the next one
*/
if (!args->stateless_rpc && count == INITIAL_FLUSH)
continue;
consume_shallow_list(args, fd[0]);
do {
ack = get_ack(fd[0], result_sha1);
if (args->verbose && ack)
fprintf(stderr, "got ack %d %s\n", ack,
sha1_to_hex(result_sha1));
switch (ack) {
case ACK:
flushes = 0;
multi_ack = 0;
retval = 0;
goto done;
case ACK_common:
case ACK_ready:
case ACK_continue: {
struct commit *commit =
lookup_commit(result_sha1);
if (!commit)
die("invalid commit %s", sha1_to_hex(result_sha1));
if (args->stateless_rpc
&& ack == ACK_common
&& !(commit->object.flags & COMMON)) {
/* We need to replay the have for this object
* on the next RPC request so the peer knows
* it is in common with us.
*/
const char *hex = sha1_to_hex(result_sha1);
packet_buf_write(&req_buf, "have %s\n", hex);
state_len = req_buf.len;
}
mark_common(commit, 0, 1);
retval = 0;
in_vain = 0;
got_continue = 1;
if (ack == ACK_ready) {
rev_list = NULL;
got_ready = 1;
}
break;
}
}
} while (ack);
flushes--;
if (got_continue && MAX_IN_VAIN < in_vain) {
if (args->verbose)
fprintf(stderr, "giving up\n");
break; /* give up */
}
}
}
done:
if (!got_ready || !no_done) {
packet_buf_write(&req_buf, "done\n");
send_request(args, fd[1], &req_buf);
}
if (args->verbose)
fprintf(stderr, "done\n");
if (retval != 0) {
multi_ack = 0;
flushes++;
}
strbuf_release(&req_buf);
consume_shallow_list(args, fd[0]);
while (flushes || multi_ack) {
int ack = get_ack(fd[0], result_sha1);
if (ack) {
if (args->verbose)
fprintf(stderr, "got ack (%d) %s\n", ack,
sha1_to_hex(result_sha1));
if (ack == ACK)
return 0;
multi_ack = 1;
continue;
}
flushes--;
}
/* it is no error to fetch into a completely empty repo */
return count ? retval : 0;
}
static struct commit_list *complete;
static int mark_complete(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
struct object *o = parse_object(sha1);
while (o && o->type == OBJ_TAG) {
struct tag *t = (struct tag *) o;
if (!t->tagged)
break; /* broken repository */
o->flags |= COMPLETE;
o = parse_object(t->tagged->sha1);
}
if (o && o->type == OBJ_COMMIT) {
struct commit *commit = (struct commit *)o;
if (!(commit->object.flags & COMPLETE)) {
commit->object.flags |= COMPLETE;
commit_list_insert_by_date(commit, &complete);
}
}
return 0;
}
static void mark_recent_complete_commits(struct fetch_pack_args *args,
unsigned long cutoff)
{
while (complete && cutoff <= complete->item->date) {
if (args->verbose)
fprintf(stderr, "Marking %s as complete\n",
sha1_to_hex(complete->item->object.sha1));
pop_most_recent_commit(&complete, COMPLETE);
}
}
static int non_matching_ref(struct string_list_item *item, void *unused)
{
if (item->util) {
item->util = NULL;
return 0;
}
else
return 1;
}
static void filter_refs(struct fetch_pack_args *args,
struct ref **refs, struct string_list *sought)
{
struct ref *newlist = NULL;
struct ref **newtail = &newlist;
struct ref *ref, *next;
int sought_pos;
sought_pos = 0;
for (ref = *refs; ref; ref = next) {
int keep = 0;
next = ref->next;
if (!memcmp(ref->name, "refs/", 5) &&
check_refname_format(ref->name + 5, 0))
; /* trash */
else {
while (sought_pos < sought->nr) {
int cmp = strcmp(ref->name, sought->items[sought_pos].string);
if (cmp < 0)
break; /* definitely do not have it */
else if (cmp == 0) {
keep = 1; /* definitely have it */
sought->items[sought_pos++].util = "matched";
break;
}
else
sought_pos++; /* might have it; keep looking */
}
}
if (! keep && args->fetch_all &&
(!args->depth || prefixcmp(ref->name, "refs/tags/")))
keep = 1;
if (keep) {
*newtail = ref;
ref->next = NULL;
newtail = &ref->next;
} else {
free(ref);
}
}
filter_string_list(sought, 0, non_matching_ref, NULL);
*refs = newlist;
}
static void mark_alternate_complete(const struct ref *ref, void *unused)
{
mark_complete(NULL, ref->old_sha1, 0, NULL);
}
static int everything_local(struct fetch_pack_args *args,
struct ref **refs, struct string_list *sought)
{
struct ref *ref;
int retval;
unsigned long cutoff = 0;
save_commit_buffer = 0;
for (ref = *refs; ref; ref = ref->next) {
struct object *o;
o = parse_object(ref->old_sha1);
if (!o)
continue;
/* We already have it -- which may mean that we were
* in sync with the other side at some time after
* that (it is OK if we guess wrong here).
*/
if (o->type == OBJ_COMMIT) {
struct commit *commit = (struct commit *)o;
if (!cutoff || cutoff < commit->date)
cutoff = commit->date;
}
}
if (!args->depth) {
for_each_ref(mark_complete, NULL);
for_each_alternate_ref(mark_alternate_complete, NULL);
if (cutoff)
mark_recent_complete_commits(args, cutoff);
}
/*
* Mark all complete remote refs as common refs.
* Don't mark them common yet; the server has to be told so first.
*/
for (ref = *refs; ref; ref = ref->next) {
struct object *o = deref_tag(lookup_object(ref->old_sha1),
NULL, 0);
if (!o || o->type != OBJ_COMMIT || !(o->flags & COMPLETE))
continue;
if (!(o->flags & SEEN)) {
rev_list_push((struct commit *)o, COMMON_REF | SEEN);
mark_common((struct commit *)o, 1, 1);
}
}
filter_refs(args, refs, sought);
for (retval = 1, ref = *refs; ref ; ref = ref->next) {
const unsigned char *remote = ref->old_sha1;
unsigned char local[20];
struct object *o;
o = lookup_object(remote);
if (!o || !(o->flags & COMPLETE)) {
retval = 0;
if (!args->verbose)
continue;
fprintf(stderr,
"want %s (%s)\n", sha1_to_hex(remote),
ref->name);
continue;
}
hashcpy(ref->new_sha1, local);
if (!args->verbose)
continue;
fprintf(stderr,
"already have %s (%s)\n", sha1_to_hex(remote),
ref->name);
}
return retval;
}
static int sideband_demux(int in, int out, void *data)
{
int *xd = data;
int ret = recv_sideband("fetch-pack", xd[0], out);
close(out);
return ret;
}
static int get_pack(struct fetch_pack_args *args,
int xd[2], char **pack_lockfile)
{
struct async demux;
const char *argv[20];
char keep_arg[256];
char hdr_arg[256];
const char **av;
int do_keep = args->keep_pack;
struct child_process cmd;
memset(&demux, 0, sizeof(demux));
if (use_sideband) {
/* xd[] is talking with upload-pack; subprocess reads from
* xd[0], spits out band#2 to stderr, and feeds us band#1
* through demux->out.
*/
demux.proc = sideband_demux;
demux.data = xd;
demux.out = -1;
if (start_async(&demux))
die("fetch-pack: unable to fork off sideband"
" demultiplexer");
}
else
demux.out = xd[0];
memset(&cmd, 0, sizeof(cmd));
cmd.argv = argv;
av = argv;
*hdr_arg = 0;
if (!args->keep_pack && unpack_limit) {
struct pack_header header;
if (read_pack_header(demux.out, &header))
die("protocol error: bad pack header");
snprintf(hdr_arg, sizeof(hdr_arg),
"--pack_header=%"PRIu32",%"PRIu32,
ntohl(header.hdr_version), ntohl(header.hdr_entries));
if (ntohl(header.hdr_entries) < unpack_limit)
do_keep = 0;
else
do_keep = 1;
}
if (do_keep) {
if (pack_lockfile)
cmd.out = -1;
*av++ = "index-pack";
*av++ = "--stdin";
if (!args->quiet && !args->no_progress)
*av++ = "-v";
if (args->use_thin_pack)
*av++ = "--fix-thin";
if (args->lock_pack || unpack_limit) {
int s = sprintf(keep_arg,
"--keep=fetch-pack %"PRIuMAX " on ", (uintmax_t) getpid());
if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
strcpy(keep_arg + s, "localhost");
*av++ = keep_arg;
}
}
else {
*av++ = "unpack-objects";
if (args->quiet || args->no_progress)
*av++ = "-q";
}
if (*hdr_arg)
*av++ = hdr_arg;
if (fetch_fsck_objects >= 0
? fetch_fsck_objects
: transfer_fsck_objects >= 0
? transfer_fsck_objects
: 0)
*av++ = "--strict";
*av++ = NULL;
cmd.in = demux.out;
cmd.git_cmd = 1;
if (start_command(&cmd))
die("fetch-pack: unable to fork off %s", argv[0]);
if (do_keep && pack_lockfile) {
*pack_lockfile = index_pack_lockfile(cmd.out);
close(cmd.out);
}
if (finish_command(&cmd))
die("%s failed", argv[0]);
if (use_sideband && finish_async(&demux))
die("error in sideband demultiplexer");
return 0;
}
static struct ref *do_fetch_pack(struct fetch_pack_args *args,
int fd[2],
const struct ref *orig_ref,
struct string_list *sought,
char **pack_lockfile)
{
struct ref *ref = copy_ref_list(orig_ref);
unsigned char sha1[20];
const char *agent_feature;
int agent_len;
sort_ref_list(&ref, ref_compare_name);
if (is_repository_shallow() && !server_supports("shallow"))
die("Server does not support shallow clients");
if (server_supports("multi_ack_detailed")) {
if (args->verbose)
fprintf(stderr, "Server supports multi_ack_detailed\n");
multi_ack = 2;
if (server_supports("no-done")) {
if (args->verbose)
fprintf(stderr, "Server supports no-done\n");
if (args->stateless_rpc)
no_done = 1;
}
}
else if (server_supports("multi_ack")) {
if (args->verbose)
fprintf(stderr, "Server supports multi_ack\n");
multi_ack = 1;
}
if (server_supports("side-band-64k")) {
if (args->verbose)
fprintf(stderr, "Server supports side-band-64k\n");
use_sideband = 2;
}
else if (server_supports("side-band")) {
if (args->verbose)
fprintf(stderr, "Server supports side-band\n");
use_sideband = 1;
}
if (!server_supports("thin-pack"))
args->use_thin_pack = 0;
if (!server_supports("no-progress"))
args->no_progress = 0;
if (!server_supports("include-tag"))
args->include_tag = 0;
if (server_supports("ofs-delta")) {
if (args->verbose)
fprintf(stderr, "Server supports ofs-delta\n");
} else
prefer_ofs_delta = 0;
if ((agent_feature = server_feature_value("agent", &agent_len))) {
agent_supported = 1;
if (args->verbose && agent_len)
fprintf(stderr, "Server version is %.*s\n",
agent_len, agent_feature);
}
if (everything_local(args, &ref, sought)) {
packet_flush(fd[1]);
goto all_done;
}
if (find_common(args, fd, sha1, ref) < 0)
if (!args->keep_pack)
/* When cloning, it is not unusual to have
* no common commit.
*/
warning("no common commits");
if (args->stateless_rpc)
packet_flush(fd[1]);
if (get_pack(args, fd, pack_lockfile))
die("git fetch-pack: fetch failed.");
all_done:
return ref;
}
static int fetch_pack_config(const char *var, const char *value, void *cb)
{
if (strcmp(var, "fetch.unpacklimit") == 0) {
fetch_unpack_limit = git_config_int(var, value);
return 0;
}
if (strcmp(var, "transfer.unpacklimit") == 0) {
transfer_unpack_limit = git_config_int(var, value);
return 0;
}
if (strcmp(var, "repack.usedeltabaseoffset") == 0) {
prefer_ofs_delta = git_config_bool(var, value);
return 0;
}
if (!strcmp(var, "fetch.fsckobjects")) {
fetch_fsck_objects = git_config_bool(var, value);
return 0;
}
if (!strcmp(var, "transfer.fsckobjects")) {
transfer_fsck_objects = git_config_bool(var, value);
return 0;
}
return git_default_config(var, value, cb);
}
static struct lock_file lock;
static void fetch_pack_setup(void)
{
static int did_setup;
if (did_setup)
return;
git_config(fetch_pack_config, NULL);
if (0 <= transfer_unpack_limit)
unpack_limit = transfer_unpack_limit;
else if (0 <= fetch_unpack_limit)
unpack_limit = fetch_unpack_limit;
did_setup = 1;
}
struct ref *fetch_pack(struct fetch_pack_args *args,
int fd[], struct child_process *conn,
const struct ref *ref,
const char *dest,
struct string_list *sought,
char **pack_lockfile)
{
struct stat st;
struct ref *ref_cpy;
fetch_pack_setup();
if (args->depth > 0) {
if (stat(git_path("shallow"), &st))
st.st_mtime = 0;
}
if (sought->nr) {
sort_string_list(sought);
string_list_remove_duplicates(sought, 0);
}
if (!ref) {
packet_flush(fd[1]);
die("no matching remote head");
}
ref_cpy = do_fetch_pack(args, fd, ref, sought, pack_lockfile);
if (args->depth > 0) {
struct cache_time mtime;
struct strbuf sb = STRBUF_INIT;
char *shallow = git_path("shallow");
int fd;
mtime.sec = st.st_mtime;
mtime.nsec = ST_MTIME_NSEC(st);
if (stat(shallow, &st)) {
if (mtime.sec)
die("shallow file was removed during fetch");
} else if (st.st_mtime != mtime.sec
#ifdef USE_NSEC
|| ST_MTIME_NSEC(st) != mtime.nsec
#endif
)
die("shallow file was changed during fetch");
fd = hold_lock_file_for_update(&lock, shallow,
LOCK_DIE_ON_ERROR);
if (!write_shallow_commits(&sb, 0)
|| write_in_full(fd, sb.buf, sb.len) != sb.len) {
unlink_or_warn(shallow);
rollback_lock_file(&lock);
} else {
commit_lock_file(&lock);
}
strbuf_release(&sb);
}
reprepare_packed_git();
return ref_cpy;
}

33
git.c
View File

@ -17,39 +17,6 @@ const char git_more_info_string[] =
static struct startup_info git_startup_info;
static int use_pager = -1;
struct pager_config {
const char *cmd;
int want;
char *value;
};
static int pager_command_config(const char *var, const char *value, void *data)
{
struct pager_config *c = data;
if (!prefixcmp(var, "pager.") && !strcmp(var + 6, c->cmd)) {
int b = git_config_maybe_bool(var, value);
if (b >= 0)
c->want = b;
else {
c->want = 1;
c->value = xstrdup(value);
}
}
return 0;
}
/* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */
int check_pager_config(const char *cmd)
{
struct pager_config c;
c.cmd = cmd;
c.want = -1;
c.value = NULL;
git_config(pager_command_config, &c);
if (c.value)
pager_program = c.value;
return c.want;
}
static void commit_pager_choice(void) {
switch (use_pager) {

View File

@ -59,9 +59,4 @@ struct tree *write_tree_from_memory(struct merge_options *o);
int parse_merge_opt(struct merge_options *out, const char *s);
/* builtin/merge.c */
int try_merge_command(const char *strategy, size_t xopts_nr,
const char **xopts, struct commit_list *common,
const char *head_arg, struct commit_list *remotes);
#endif

112
merge.c Normal file
View File

@ -0,0 +1,112 @@
#include "cache.h"
#include "commit.h"
#include "run-command.h"
#include "resolve-undo.h"
#include "tree-walk.h"
#include "unpack-trees.h"
#include "dir.h"
static const char *merge_argument(struct commit *commit)
{
if (commit)
return sha1_to_hex(commit->object.sha1);
else
return EMPTY_TREE_SHA1_HEX;
}
int try_merge_command(const char *strategy, size_t xopts_nr,
const char **xopts, struct commit_list *common,
const char *head_arg, struct commit_list *remotes)
{
const char **args;
int i = 0, x = 0, ret;
struct commit_list *j;
struct strbuf buf = STRBUF_INIT;
args = xmalloc((4 + xopts_nr + commit_list_count(common) +
commit_list_count(remotes)) * sizeof(char *));
strbuf_addf(&buf, "merge-%s", strategy);
args[i++] = buf.buf;
for (x = 0; x < xopts_nr; x++) {
char *s = xmalloc(strlen(xopts[x])+2+1);
strcpy(s, "--");
strcpy(s+2, xopts[x]);
args[i++] = s;
}
for (j = common; j; j = j->next)
args[i++] = xstrdup(merge_argument(j->item));
args[i++] = "--";
args[i++] = head_arg;
for (j = remotes; j; j = j->next)
args[i++] = xstrdup(merge_argument(j->item));
args[i] = NULL;
ret = run_command_v_opt(args, RUN_GIT_CMD);
strbuf_release(&buf);
i = 1;
for (x = 0; x < xopts_nr; x++)
free((void *)args[i++]);
for (j = common; j; j = j->next)
free((void *)args[i++]);
i += 2;
for (j = remotes; j; j = j->next)
free((void *)args[i++]);
free(args);
discard_cache();
if (read_cache() < 0)
die(_("failed to read the cache"));
resolve_undo_clear();
return ret;
}
int checkout_fast_forward(const unsigned char *head,
const unsigned char *remote,
int overwrite_ignore)
{
struct tree *trees[MAX_UNPACK_TREES];
struct unpack_trees_options opts;
struct tree_desc t[MAX_UNPACK_TREES];
int i, fd, nr_trees = 0;
struct dir_struct dir;
struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
refresh_cache(REFRESH_QUIET);
fd = hold_locked_index(lock_file, 1);
memset(&trees, 0, sizeof(trees));
memset(&opts, 0, sizeof(opts));
memset(&t, 0, sizeof(t));
if (overwrite_ignore) {
memset(&dir, 0, sizeof(dir));
dir.flags |= DIR_SHOW_IGNORED;
setup_standard_excludes(&dir);
opts.dir = &dir;
}
opts.head_idx = 1;
opts.src_index = &the_index;
opts.dst_index = &the_index;
opts.update = 1;
opts.verbose_update = 1;
opts.merge = 1;
opts.fn = twoway_merge;
setup_unpack_trees_porcelain(&opts, "merge");
trees[nr_trees] = parse_tree_indirect(head);
if (!trees[nr_trees++])
return -1;
trees[nr_trees] = parse_tree_indirect(remote);
if (!trees[nr_trees++])
return -1;
for (i = 0; i < nr_trees; i++) {
parse_tree(trees[i]);
init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
}
if (unpack_trees(nr_trees, t, &opts))
return -1;
if (write_cache(fd, active_cache, active_nr) ||
commit_locked_index(lock_file))
die(_("unable to write new index file"));
return 0;
}

34
pager.c
View File

@ -6,6 +6,12 @@
#define DEFAULT_PAGER "less"
#endif
struct pager_config {
const char *cmd;
int want;
char *value;
};
/*
* This is split up from the rest of git so that we can do
* something different on Windows.
@ -141,3 +147,31 @@ int decimal_width(int number)
i *= 10;
return width;
}
static int pager_command_config(const char *var, const char *value, void *data)
{
struct pager_config *c = data;
if (!prefixcmp(var, "pager.") && !strcmp(var + 6, c->cmd)) {
int b = git_config_maybe_bool(var, value);
if (b >= 0)
c->want = b;
else {
c->want = 1;
c->value = xstrdup(value);
}
}
return 0;
}
/* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */
int check_pager_config(const char *cmd)
{
struct pager_config c;
c.cmd = cmd;
c.want = -1;
c.value = NULL;
git_config(pager_command_config, &c);
if (c.value)
pager_program = c.value;
return c.want;
}

344
send-pack.c Normal file
View File

@ -0,0 +1,344 @@
#include "builtin.h"
#include "commit.h"
#include "refs.h"
#include "pkt-line.h"
#include "sideband.h"
#include "run-command.h"
#include "remote.h"
#include "send-pack.h"
#include "quote.h"
#include "transport.h"
#include "version.h"
static int feed_object(const unsigned char *sha1, int fd, int negative)
{
char buf[42];
if (negative && !has_sha1_file(sha1))
return 1;
memcpy(buf + negative, sha1_to_hex(sha1), 40);
if (negative)
buf[0] = '^';
buf[40 + negative] = '\n';
return write_or_whine(fd, buf, 41 + negative, "send-pack: send refs");
}
/*
* Make a pack stream and spit it out into file descriptor fd
*/
static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *extra, struct send_pack_args *args)
{
/*
* The child becomes pack-objects --revs; we feed
* the revision parameters to it via its stdin and
* let its stdout go back to the other end.
*/
const char *argv[] = {
"pack-objects",
"--all-progress-implied",
"--revs",
"--stdout",
NULL,
NULL,
NULL,
NULL,
NULL,
};
struct child_process po;
int i;
i = 4;
if (args->use_thin_pack)
argv[i++] = "--thin";
if (args->use_ofs_delta)
argv[i++] = "--delta-base-offset";
if (args->quiet || !args->progress)
argv[i++] = "-q";
if (args->progress)
argv[i++] = "--progress";
memset(&po, 0, sizeof(po));
po.argv = argv;
po.in = -1;
po.out = args->stateless_rpc ? -1 : fd;
po.git_cmd = 1;
if (start_command(&po))
die_errno("git pack-objects failed");
/*
* We feed the pack-objects we just spawned with revision
* parameters by writing to the pipe.
*/
for (i = 0; i < extra->nr; i++)
if (!feed_object(extra->array[i], po.in, 1))
break;
while (refs) {
if (!is_null_sha1(refs->old_sha1) &&
!feed_object(refs->old_sha1, po.in, 1))
break;
if (!is_null_sha1(refs->new_sha1) &&
!feed_object(refs->new_sha1, po.in, 0))
break;
refs = refs->next;
}
close(po.in);
if (args->stateless_rpc) {
char *buf = xmalloc(LARGE_PACKET_MAX);
while (1) {
ssize_t n = xread(po.out, buf, LARGE_PACKET_MAX);
if (n <= 0)
break;
send_sideband(fd, -1, buf, n, LARGE_PACKET_MAX);
}
free(buf);
close(po.out);
po.out = -1;
}
if (finish_command(&po))
return -1;
return 0;
}
static int receive_status(int in, struct ref *refs)
{
struct ref *hint;
char line[1000];
int ret = 0;
int len = packet_read_line(in, line, sizeof(line));
if (len < 10 || memcmp(line, "unpack ", 7))
return error("did not receive remote status");
if (memcmp(line, "unpack ok\n", 10)) {
char *p = line + strlen(line) - 1;
if (*p == '\n')
*p = '\0';
error("unpack failed: %s", line + 7);
ret = -1;
}
hint = NULL;
while (1) {
char *refname;
char *msg;
len = packet_read_line(in, line, sizeof(line));
if (!len)
break;
if (len < 3 ||
(memcmp(line, "ok ", 3) && memcmp(line, "ng ", 3))) {
fprintf(stderr, "protocol error: %s\n", line);
ret = -1;
break;
}
line[strlen(line)-1] = '\0';
refname = line + 3;
msg = strchr(refname, ' ');
if (msg)
*msg++ = '\0';
/* first try searching at our hint, falling back to all refs */
if (hint)
hint = find_ref_by_name(hint, refname);
if (!hint)
hint = find_ref_by_name(refs, refname);
if (!hint) {
warning("remote reported status on unknown ref: %s",
refname);
continue;
}
if (hint->status != REF_STATUS_EXPECTING_REPORT) {
warning("remote reported status on unexpected ref: %s",
refname);
continue;
}
if (line[0] == 'o' && line[1] == 'k')
hint->status = REF_STATUS_OK;
else {
hint->status = REF_STATUS_REMOTE_REJECT;
ret = -1;
}
if (msg)
hint->remote_status = xstrdup(msg);
/* start our next search from the next ref */
hint = hint->next;
}
return ret;
}
static int sideband_demux(int in, int out, void *data)
{
int *fd = data, ret;
#ifdef NO_PTHREADS
close(fd[1]);
#endif
ret = recv_sideband("send-pack", fd[0], out);
close(out);
return ret;
}
int send_pack(struct send_pack_args *args,
int fd[], struct child_process *conn,
struct ref *remote_refs,
struct extra_have_objects *extra_have)
{
int in = fd[0];
int out = fd[1];
struct strbuf req_buf = STRBUF_INIT;
struct ref *ref;
int new_refs;
int allow_deleting_refs = 0;
int status_report = 0;
int use_sideband = 0;
int quiet_supported = 0;
int agent_supported = 0;
unsigned cmds_sent = 0;
int ret;
struct async demux;
/* Does the other end support the reporting? */
if (server_supports("report-status"))
status_report = 1;
if (server_supports("delete-refs"))
allow_deleting_refs = 1;
if (server_supports("ofs-delta"))
args->use_ofs_delta = 1;
if (server_supports("side-band-64k"))
use_sideband = 1;
if (server_supports("quiet"))
quiet_supported = 1;
if (server_supports("agent"))
agent_supported = 1;
if (!remote_refs) {
fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
"Perhaps you should specify a branch such as 'master'.\n");
return 0;
}
/*
* Finally, tell the other end!
*/
new_refs = 0;
for (ref = remote_refs; ref; ref = ref->next) {
if (!ref->peer_ref && !args->send_mirror)
continue;
/* Check for statuses set by set_ref_status_for_push() */
switch (ref->status) {
case REF_STATUS_REJECT_NONFASTFORWARD:
case REF_STATUS_UPTODATE:
continue;
default:
; /* do nothing */
}
if (ref->deletion && !allow_deleting_refs) {
ref->status = REF_STATUS_REJECT_NODELETE;
continue;
}
if (!ref->deletion)
new_refs++;
if (args->dry_run) {
ref->status = REF_STATUS_OK;
} else {
char *old_hex = sha1_to_hex(ref->old_sha1);
char *new_hex = sha1_to_hex(ref->new_sha1);
int quiet = quiet_supported && (args->quiet || !args->progress);
if (!cmds_sent && (status_report || use_sideband ||
quiet || agent_supported)) {
packet_buf_write(&req_buf,
"%s %s %s%c%s%s%s%s%s",
old_hex, new_hex, ref->name, 0,
status_report ? " report-status" : "",
use_sideband ? " side-band-64k" : "",
quiet ? " quiet" : "",
agent_supported ? " agent=" : "",
agent_supported ? git_user_agent_sanitized() : ""
);
}
else
packet_buf_write(&req_buf, "%s %s %s",
old_hex, new_hex, ref->name);
ref->status = status_report ?
REF_STATUS_EXPECTING_REPORT :
REF_STATUS_OK;
cmds_sent++;
}
}
if (args->stateless_rpc) {
if (!args->dry_run && cmds_sent) {
packet_buf_flush(&req_buf);
send_sideband(out, -1, req_buf.buf, req_buf.len, LARGE_PACKET_MAX);
}
} else {
safe_write(out, req_buf.buf, req_buf.len);
packet_flush(out);
}
strbuf_release(&req_buf);
if (use_sideband && cmds_sent) {
memset(&demux, 0, sizeof(demux));
demux.proc = sideband_demux;
demux.data = fd;
demux.out = -1;
if (start_async(&demux))
die("send-pack: unable to fork off sideband demultiplexer");
in = demux.out;
}
if (new_refs && cmds_sent) {
if (pack_objects(out, remote_refs, extra_have, args) < 0) {
for (ref = remote_refs; ref; ref = ref->next)
ref->status = REF_STATUS_NONE;
if (args->stateless_rpc)
close(out);
if (git_connection_is_socket(conn))
shutdown(fd[0], SHUT_WR);
if (use_sideband)
finish_async(&demux);
return -1;
}
}
if (args->stateless_rpc && cmds_sent)
packet_flush(out);
if (status_report && cmds_sent)
ret = receive_status(in, remote_refs);
else
ret = 0;
if (args->stateless_rpc)
packet_flush(out);
if (use_sideband && cmds_sent) {
if (finish_async(&demux)) {
error("error in sideband demultiplexer");
ret = -1;
}
close(demux.out);
}
if (ret < 0)
return ret;
if (args->porcelain)
return 0;
for (ref = remote_refs; ref; ref = ref->next) {
switch (ref->status) {
case REF_STATUS_NONE:
case REF_STATUS_UPTODATE:
case REF_STATUS_OK:
break;
default:
return -1;
}
}
return 0;
}

View File

@ -191,7 +191,7 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from)
struct ref_lock *ref_lock;
read_cache();
if (checkout_fast_forward(from, to))
if (checkout_fast_forward(from, to, 1))
exit(1); /* the callee should have complained already */
ref_lock = lock_any_ref_for_update("HEAD", from, 0);
return write_ref_sha1(ref_lock, to, "cherry-pick");

View File

@ -175,4 +175,9 @@ void transport_print_push_status(const char *dest, struct ref *refs,
typedef void alternate_ref_fn(const struct ref *, void *);
extern void for_each_alternate_ref(alternate_ref_fn, void *);
struct send_pack_args;
extern int send_pack(struct send_pack_args *args,
int fd[], struct child_process *conn,
struct ref *remote_refs,
struct extra_have_objects *extra_have);
#endif