Output excluded boundary commits. Boundary commits are
prefixed with `-`.
+ifdef::git-rev-list[]
+--use-bitmap-index::
+
+ Try to speed up the traversal using the pack bitmap index (if
+ one is available). Note that when traversing with `--objects`,
+ trees and blobs will not have their associated path printed.
+endif::git-rev-list[]
+
--
History Simplification
This implies the `--topo-order` option by default, but the
`--date-order` option may also be specified.
+ --show-linear-break[=<barrier>]::
+ When --graph is not used, all history branches are flattened
+ which can make it hard to see that the two consecutive commits
+ do not belong to a linear branch. This option puts a barrier
+ in between them in that case. If `<barrier>` is specified, it
+ is the string that will be shown instead of the default one.
+
ifdef::git-rev-list[]
--count::
Print a number stating how many commits would have been
static const char *argv_show_branch[] = {"show-branch", NULL, NULL};
static const char *argv_update_ref[] = {"update-ref", "--no-deref", "BISECT_HEAD", NULL, NULL};
- /* bits #0-15 in revision.h */
-
+ /* Remember to update object flag allocation in object.h */
#define COUNTED (1u<<16)
/*
static int bisect_checkout(char *bisect_rev_hex, int no_checkout)
{
- int res;
mark_expected_rev(bisect_rev_hex);
die("update-ref --no-deref HEAD failed on %s",
bisect_rev_hex);
} else {
+ int res;
res = run_command_v_opt(argv_checkout, RUN_GIT_CMD);
if (res)
exit(res);
#define BLAME_DEFAULT_MOVE_SCORE 20
#define BLAME_DEFAULT_COPY_SCORE 40
- /* bits #0..7 in revision.h, #8..11 used for merge_bases() in commit.c */
+ /* Remember to update object flag allocation in object.h */
#define METAINFO_SHOWN (1u<<12)
#define MORE_THAN_ONE_PATH (1u<<13)
* scoreboard structure, sorted by the target line number.
*/
struct blame_entry {
- struct blame_entry *prev;
struct blame_entry *next;
/* the first line of this group in the final image;
int *lineno;
};
-static inline int same_suspect(struct origin *a, struct origin *b)
-{
- if (a == b)
- return 1;
- if (a->commit != b->commit)
- return 0;
- return !strcmp(a->path, b->path);
-}
-
static void sanity_check_refcnt(struct scoreboard *);
/*
struct blame_entry *ent, *next;
for (ent = sb->ent; ent && (next = ent->next); ent = next) {
- if (same_suspect(ent->suspect, next->suspect) &&
+ if (ent->suspect == next->suspect &&
ent->guilty == next->guilty &&
ent->s_lno + ent->num_lines == next->s_lno) {
ent->num_lines += next->num_lines;
ent->next = next->next;
- if (ent->next)
- ent->next->prev = ent;
origin_decref(next->suspect);
free(next);
ent->score = 0;
prev = ent;
/* prev, if not NULL, is the last one that is below e */
- e->prev = prev;
+
if (prev) {
e->next = prev->next;
prev->next = e;
e->next = sb->ent;
sb->ent = e;
}
- if (e->next)
- e->next->prev = e;
}
/*
*/
static void dup_entry(struct blame_entry *dst, struct blame_entry *src)
{
- struct blame_entry *p, *n;
+ struct blame_entry *n;
- p = dst->prev;
n = dst->next;
origin_incref(src->suspect);
origin_decref(dst->suspect);
memcpy(dst, src, sizeof(*src));
- dst->prev = p;
dst->next = n;
dst->score = 0;
}
int last_in_target = -1;
for (e = sb->ent; e; e = e->next) {
- if (e->guilty || !same_suspect(e->suspect, target))
+ if (e->guilty || e->suspect != target)
continue;
if (last_in_target < e->s_lno + e->num_lines)
last_in_target = e->s_lno + e->num_lines;
struct blame_entry *e;
for (e = sb->ent; e; e = e->next) {
- if (e->guilty || !same_suspect(e->suspect, target))
+ if (e->guilty || e->suspect != target)
continue;
if (same <= e->s_lno)
continue;
mmfile_t *file_p)
{
const char *cp;
- int cnt;
mmfile_t file_o;
struct handle_split_cb_data d;
*/
cp = nth_line(sb, ent->lno);
file_o.ptr = (char *) cp;
- cnt = ent->num_lines;
-
- while (cnt && cp < sb->final_buf + sb->final_buf_size) {
- if (*cp++ == '\n')
- cnt--;
- }
- file_o.size = cp - file_o.ptr;
+ file_o.size = nth_line(sb, ent->lno + ent->num_lines) - cp;
/*
* file_o is a part of final image we are annotating.
while (made_progress) {
made_progress = 0;
for (e = sb->ent; e; e = e->next) {
- if (e->guilty || !same_suspect(e->suspect, target) ||
+ if (e->guilty || e->suspect != target ||
ent_score(sb, e) < blame_move_score)
continue;
find_copy_in_blob(sb, e, parent, split, &file_p);
for (e = sb->ent, num_ents = 0; e; e = e->next)
if (!e->scanned && !e->guilty &&
- same_suspect(e->suspect, target) &&
+ e->suspect == target &&
min_score < ent_score(sb, e))
num_ents++;
if (num_ents) {
blame_list = xcalloc(num_ents, sizeof(struct blame_list));
for (e = sb->ent, i = 0; e; e = e->next)
if (!e->scanned && !e->guilty &&
- same_suspect(e->suspect, target) &&
+ e->suspect == target &&
min_score < ent_score(sb, e))
blame_list[i++].ent = e;
}
origin->file.ptr = NULL;
}
for (e = sb->ent; e; e = e->next) {
- if (!same_suspect(e->suspect, origin))
+ if (e->suspect != origin)
continue;
origin_incref(porigin);
origin_decref(e->suspect);
/* Take responsibility for the remaining entries */
for (ent = sb->ent; ent; ent = ent->next)
- if (same_suspect(ent->suspect, suspect))
+ if (ent->suspect == suspect)
found_guilty_entry(ent);
origin_decref(suspect);
int show_raw_time)
{
static char time_buf[128];
- const char *time_str;
- int time_len;
- int tz;
if (show_raw_time) {
snprintf(time_buf, sizeof(time_buf), "%lu %s", time, tz_str);
}
else {
+ const char *time_str;
+ int time_len;
+ int tz;
tz = atoi(tz_str);
time_str = show_date(time, tz, blame_date_mode);
time_len = strlen(time_str);
{
const char *buf = sb->final_buf;
unsigned long len = sb->final_buf_size;
- int num = 0, incomplete = 0, bol = 1;
+ const char *end = buf + len;
+ const char *p;
+ int *lineno;
+ int num = 0, incomplete = 0;
- if (len && buf[len-1] != '\n')
- incomplete++; /* incomplete line at the end */
- while (len--) {
- if (bol) {
- sb->lineno = xrealloc(sb->lineno,
- sizeof(int *) * (num + 1));
- sb->lineno[num] = buf - sb->final_buf;
- bol = 0;
- }
- if (*buf++ == '\n') {
+ for (p = buf;;) {
+ p = memchr(p, '\n', end - p);
+ if (p) {
+ p++;
num++;
- bol = 1;
+ continue;
}
+ break;
}
- sb->lineno = xrealloc(sb->lineno,
- sizeof(int *) * (num + incomplete + 1));
- sb->lineno[num + incomplete] = buf - sb->final_buf;
+
+ if (len && end[-1] != '\n')
+ incomplete++; /* incomplete line at the end */
+
+ sb->lineno = xmalloc(sizeof(*sb->lineno) * (num + incomplete + 1));
+ lineno = sb->lineno;
+
+ *lineno++ = 0;
+ for (p = buf;;) {
+ p = memchr(p, '\n', end - p);
+ if (p) {
+ p++;
+ *lineno++ = p - buf;
+ continue;
+ }
+ break;
+ }
+
+ if (incomplete)
+ *lineno++ = len;
+
sb->num_lines = num + incomplete;
return sb->num_lines;
}
ent->suspect = o;
ent->s_lno = bottom;
ent->next = next;
- if (next)
- next->prev = ent;
origin_incref(o);
}
origin_decref(o);
static void add_to_ref_list(const unsigned char *sha1, const char *name,
struct ref_list *list)
{
- if (list->nr + 1 >= list->alloc) {
- list->alloc = alloc_nr(list->nr + 1);
- list->list = xrealloc(list->list,
- list->alloc * sizeof(list->list[0]));
- }
- memcpy(list->list[list->nr].sha1, sha1, 20);
+ ALLOC_GROW(list->list, list->nr + 1, list->alloc);
+ hashcpy(list->list[list->nr].sha1, sha1);
list->list[list->nr].name = xstrdup(name);
list->nr++;
}
return 0;
}
+ /* Remember to update object flag allocation in object.h */
#define PREREQ_MARK (1u<<16)
int verify_bundle(struct bundle_header *header, int verbose)
#include "mergesort.h"
#include "commit-slab.h"
#include "prio-queue.h"
+#include "sha1-lookup.h"
static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
static struct commit_graft **commit_graft;
static int commit_graft_alloc, commit_graft_nr;
+static const unsigned char *commit_graft_sha1_access(size_t index, void *table)
+{
+ struct commit_graft **commit_graft_table = table;
+ return commit_graft_table[index]->sha1;
+}
+
static int commit_graft_pos(const unsigned char *sha1)
{
- int lo, hi;
- lo = 0;
- hi = commit_graft_nr;
- while (lo < hi) {
- int mi = (lo + hi) / 2;
- struct commit_graft *graft = commit_graft[mi];
- int cmp = hashcmp(sha1, graft->sha1);
- if (!cmp)
- return mi;
- if (cmp < 0)
- hi = mi;
- else
- lo = mi + 1;
- }
- return -lo - 1;
+ return sha1_pos(sha1, commit_graft, commit_graft_nr,
+ commit_graft_sha1_access);
}
int register_commit_graft(struct commit_graft *graft, int ignore_dups)
return 1;
}
pos = -pos - 1;
- if (commit_graft_alloc <= ++commit_graft_nr) {
- commit_graft_alloc = alloc_nr(commit_graft_alloc);
- commit_graft = xrealloc(commit_graft,
- sizeof(*commit_graft) *
- commit_graft_alloc);
- }
+ ALLOC_GROW(commit_graft, commit_graft_nr + 1, commit_graft_alloc);
+ commit_graft_nr++;
if (pos < commit_graft_nr)
memmove(commit_graft + pos + 1,
commit_graft + pos,
static void record_author_date(struct author_date_slab *author_date,
struct commit *commit)
{
- const char *buf, *line_end;
+ const char *buf, *line_end, *ident_line;
char *buffer = NULL;
struct ident_split ident;
char *date_end;
buf;
buf = line_end + 1) {
line_end = strchrnul(buf, '\n');
- if (!starts_with(buf, "author ")) {
+ ident_line = skip_prefix(buf, "author ");
+ if (!ident_line) {
if (!line_end[0] || line_end[1] == '\n')
return; /* end of header */
continue;
}
if (split_ident_line(&ident,
- buf + strlen("author "),
- line_end - (buf + strlen("author "))) ||
+ ident_line, line_end - ident_line) ||
!ident.date_begin || !ident.date_end)
goto fail_exit; /* malformed "author" line */
break;
/* merge-base stuff */
- /* bits #0..15 in revision.h */
+ /* Remember to update object flag allocation in object.h */
#define PARENT1 (1u<<16)
#define PARENT2 (1u<<17)
#define STALE (1u<<18)
for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) {
const char *found, *next;
- if (starts_with(buf, sigcheck_gpg_status[i].check + 1)) {
- /* At the very beginning of the buffer */
- found = buf + strlen(sigcheck_gpg_status[i].check + 1);
- } else {
+ found = skip_prefix(buf, sigcheck_gpg_status[i].check + 1);
+ if (!found) {
found = strstr(buf, sigcheck_gpg_status[i].check);
if (!found)
continue;
static struct lock_file shallow_lock;
static const char *alternate_shallow_file;
+ /* Remember to update object flag allocation in object.h */
#define COMPLETE (1U << 0)
#define COMMON (1U << 1)
#define COMMON_REF (1U << 2)
}
strbuf_release(&req_buf);
- consume_shallow_list(args, fd[0]);
+ if (!got_ready || !no_done)
+ consume_shallow_list(args, fd[0]);
while (flushes || multi_ack) {
int ack = get_ack(fd[0], result_sha1);
if (ack) {
next = ref->next;
if (!memcmp(ref->name, "refs/", 5) &&
- check_refname_format(ref->name + 5, 0))
+ check_refname_format(ref->name, 0))
; /* trash */
else {
while (i < nr_sought) {
if (!si->shallow || !si->shallow->nr)
return;
- if (alternate_shallow_file) {
- /*
- * The temporary shallow file is only useful for
- * index-pack and unpack-objects because it may
- * contain more roots than we want. Delete it.
- */
- if (*alternate_shallow_file)
- unlink(alternate_shallow_file);
- free((char *)alternate_shallow_file);
- }
-
if (args->cloning) {
/*
* remote is shallow, but this is a clone, there are
if (starts_with(refname, "refs/replace/")) {
unsigned char original_sha1[20];
- if (!read_replace_refs)
+ if (!check_replace_refs)
return 0;
if (get_sha1_hex(refname + 13, original_sha1)) {
warning("invalid replace ref %s", refname);
if (opt->line_level_traverse)
return line_log_print(opt, commit);
+ if (opt->track_linear && !opt->linear && !opt->reverse_output_stage)
+ printf("\n%s\n", opt->break_bar);
shown = log_tree_diff(opt, commit, &log);
if (!shown && opt->loginfo && opt->always_show_header) {
log.parent = NULL;
show_log(opt);
shown = 1;
}
+ if (opt->track_linear && !opt->linear && opt->reverse_output_stage)
+ printf("\n%s\n", opt->break_bar);
opt->loginfo = NULL;
maybe_flush_or_die(stdout, "stdout");
return shown;
#define OBJECT_ARRAY_INIT { 0, 0, NULL }
#define TYPE_BITS 3
+ /*
+ * object flag allocation:
+ * revision.h: 0---------10 26
+ * fetch-pack.c: 0---4
+ * walker.c: 0-2
+ * upload-pack.c: 11----------------19
+ * builtin/blame.c: 12-13
+ * bisect.c: 16
+ * bundle.c: 16
+ * http-push.c: 16-----19
+ * commit.c: 16-----19
+ * sha1_name.c: 20
+ */
#define FLAG_BITS 27
/*
extern const char *typename(unsigned int type);
extern int type_from_string(const char *str);
+/*
+ * Return the current number of buckets in the object hashmap.
+ */
extern unsigned int get_max_object_index(void);
+
+/*
+ * Return the object from the specified bucket in the object hashmap.
+ */
extern struct object *get_indexed_object(unsigned int);
/*
#include "line-log.h"
#include "mailmap.h"
#include "commit-slab.h"
+#include "dir.h"
volatile show_early_output_fn_t show_early_output;
blob->object.flags |= UNINTERESTING;
}
-void mark_tree_uninteresting(struct tree *tree)
+static void mark_tree_contents_uninteresting(struct tree *tree)
{
struct tree_desc desc;
struct name_entry entry;
struct object *obj = &tree->object;
- if (!tree)
- return;
- if (obj->flags & UNINTERESTING)
- return;
- obj->flags |= UNINTERESTING;
if (!has_sha1_file(obj->sha1))
return;
if (parse_tree(tree) < 0)
free_tree_buffer(tree);
}
+void mark_tree_uninteresting(struct tree *tree)
+{
+ struct object *obj = &tree->object;
+
+ if (!tree)
+ return;
+ if (obj->flags & UNINTERESTING)
+ return;
+ obj->flags |= UNINTERESTING;
+ mark_tree_contents_uninteresting(tree);
+}
+
void mark_parents_uninteresting(struct commit *commit)
{
struct commit_list *parents = NULL, *l;
return NULL;
die("bad object %s", sha1_to_hex(tag->tagged->sha1));
}
+ object->flags |= flags;
}
/*
if (parse_commit(commit) < 0)
die("unable to parse commit %s", name);
if (flags & UNINTERESTING) {
- commit->object.flags |= UNINTERESTING;
mark_parents_uninteresting(commit);
revs->limited = 1;
}
if (!revs->tree_objects)
return NULL;
if (flags & UNINTERESTING) {
- mark_tree_uninteresting(tree);
+ mark_tree_contents_uninteresting(tree);
return NULL;
}
add_pending_object(revs, object, "");
* Blob object? You know the drill by now..
*/
if (object->type == OBJ_BLOB) {
- struct blob *blob = (struct blob *)object;
if (!revs->blob_objects)
return NULL;
- if (flags & UNINTERESTING) {
- mark_blob_uninteresting(blob);
+ if (flags & UNINTERESTING)
return NULL;
- }
add_pending_object(revs, object, "");
return NULL;
}
static int rev_same_tree_as_empty(struct rev_info *revs, struct commit *commit)
{
int retval;
- void *tree;
- unsigned long size;
- struct tree_desc empty, real;
struct tree *t1 = commit->tree;
if (!t1)
return 0;
- tree = read_object_with_reference(t1->object.sha1, tree_type, &size, NULL);
- if (!tree)
- return 0;
- init_tree_desc(&real, tree, size);
- init_tree_desc(&empty, "", 0);
-
tree_difference = REV_TREE_SAME;
DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);
- retval = diff_tree(&empty, &real, "", &revs->pruning);
- free(tree);
+ retval = diff_tree_sha1(NULL, t1->object.sha1, "", &revs->pruning);
return retval >= 0 && (tree_difference == REV_TREE_SAME);
}
return 0;
commit->object.flags |= ADDED;
+ if (revs->include_check &&
+ !revs->include_check(commit, revs->include_check_data))
+ return 0;
+
/*
* If the commit is uninteresting, don't try to
* prune parents - we want the maximal uninteresting
if (!ref_excludes)
return 0;
for_each_string_list_item(item, ref_excludes) {
- if (!fnmatch(item->string, path, 0))
+ if (!wildmatch(item->string, path, 0, NULL))
return 1;
}
return 0;
const struct cache_entry *ce = active_cache[i];
if (!ce_stage(ce))
continue;
- if (ce_path_match(ce, &revs->prune_data)) {
+ if (ce_path_match(ce, &revs->prune_data, NULL)) {
prune_num++;
prune = xrealloc(prune, sizeof(*prune) * prune_num);
prune[prune_num-2] = ce->name;
{
struct strbuf sb;
int seen_dashdash = 0;
+ int save_warning;
+
+ save_warning = warn_on_object_refname_ambiguity;
+ warn_on_object_refname_ambiguity = 0;
strbuf_init(&sb, 1000);
while (strbuf_getwholeline(&sb, stdin, '\n') != EOF) {
}
if (seen_dashdash)
read_pathspec_from_stdin(revs, &sb, prune);
+
strbuf_release(&sb);
+ warn_on_object_refname_ambiguity = save_warning;
}
static void add_grep(struct rev_info *revs, const char *ptn, enum grep_pat_token what)
revs->notes_opt.use_default_notes = 1;
} else if (!strcmp(arg, "--show-signature")) {
revs->show_signature = 1;
+ } else if (!strcmp(arg, "--show-linear-break") ||
+ starts_with(arg, "--show-linear-break=")) {
+ if (starts_with(arg, "--show-linear-break="))
+ revs->break_bar = xstrdup(arg + 20);
+ else
+ revs->break_bar = " ..........";
+ revs->track_linear = 1;
+ revs->track_first_time = 1;
} else if (starts_with(arg, "--show-notes=") ||
starts_with(arg, "--notes=")) {
struct strbuf buf = STRBUF_INIT;
unkv[(*unkc)++] = arg;
return opts;
}
+ if (revs->graph && revs->track_linear)
+ die("--show-linear-break and --graph are incompatible");
return 1;
}
return action;
}
+ static void track_linear(struct rev_info *revs, struct commit *commit)
+ {
+ if (revs->track_first_time) {
+ revs->linear = 1;
+ revs->track_first_time = 0;
+ } else {
+ struct commit_list *p;
+ for (p = revs->previous_parents; p; p = p->next)
+ if (p->item == NULL || /* first commit */
+ !hashcmp(p->item->object.sha1, commit->object.sha1))
+ break;
+ revs->linear = p != NULL;
+ }
+ if (revs->reverse) {
+ if (revs->linear)
+ commit->object.flags |= TRACK_LINEAR;
+ }
+ free_commit_list(revs->previous_parents);
+ revs->previous_parents = copy_commit_list(commit->parents);
+ }
+
static struct commit *get_revision_1(struct rev_info *revs)
{
if (!revs->commits)
die("Failed to simplify parents of commit %s",
sha1_to_hex(commit->object.sha1));
default:
+ if (revs->track_linear)
+ track_linear(revs, commit);
return commit;
}
} while (revs->commits);
revs->reverse_output_stage = 1;
}
- if (revs->reverse_output_stage)
- return pop_commit(&revs->commits);
+ if (revs->reverse_output_stage) {
+ c = pop_commit(&revs->commits);
+ if (revs->track_linear)
+ revs->linear = !!(c && c->object.flags & TRACK_LINEAR);
+ return c;
+ }
c = get_revision_internal(revs);
if (c && revs->graph)
graph_update(revs->graph, c);
- if (!c)
+ if (!c) {
free_saved_parents(revs);
+ if (revs->previous_parents) {
+ free_commit_list(revs->previous_parents);
+ revs->previous_parents = NULL;
+ }
+ }
return c;
}
#include "commit.h"
#include "diff.h"
+ /* Remember to update object flag allocation in object.h */
#define SEEN (1u<<0)
#define UNINTERESTING (1u<<1)
#define TREESAME (1u<<2)
#define SYMMETRIC_LEFT (1u<<8)
#define PATCHSAME (1u<<9)
#define BOTTOM (1u<<10)
- #define ALL_REV_FLAGS ((1u<<11)-1)
+ #define TRACK_LINEAR (1u<<26)
+ #define ALL_REV_FLAGS (((1u<<11)-1) | TRACK_LINEAR)
#define DECORATE_SHORT_REFS 1
#define DECORATE_FULL_REFS 2
preserve_subject:1;
unsigned int disable_stdin:1;
unsigned int leak_pending:1;
+ /* --show-linear-break */
+ unsigned int track_linear:1,
+ track_first_time:1,
+ linear:1;
enum date_mode date_mode;
unsigned long min_age;
int min_parents;
int max_parents;
+ int (*include_check)(struct commit *, void *);
+ void *include_check_data;
/* diff info for patches and for paths limiting */
struct diff_options diffopt;
/* copies of the parent lists, for --full-diff display */
struct saved_parents *saved_parents_slab;
+
+ struct commit_list *previous_parents;
+ const char *break_bar;
};
extern int ref_excluded(struct string_list *, const char *path);
}
static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned lookup_flags);
-static int interpret_nth_prior_checkout(const char *name, struct strbuf *buf);
+static int interpret_nth_prior_checkout(const char *name, int namelen, struct strbuf *buf);
static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
{
struct strbuf buf = STRBUF_INIT;
int detached;
- if (interpret_nth_prior_checkout(str, &buf) > 0) {
+ if (interpret_nth_prior_checkout(str, len, &buf) > 0) {
detached = (buf.len == 40 && !get_sha1_hex(buf.buf, sha1));
strbuf_release(&buf);
if (detached)
* For future extension, ':/!' is reserved. If you want to match a message
* beginning with a '!', you have to repeat the exclamation mark.
*/
+
+ /* Remember to update object flag allocation in object.h */
#define ONELINE_SEEN (1u<<20)
static int handle_one_ref(const char *path,
* Parse @{-N} syntax, return the number of characters parsed
* if successful; otherwise signal an error with negative value.
*/
-static int interpret_nth_prior_checkout(const char *name, struct strbuf *buf)
+static int interpret_nth_prior_checkout(const char *name, int namelen,
+ struct strbuf *buf)
{
long nth;
int retval;
const char *brace;
char *num_end;
+ if (namelen < 4)
+ return -1;
if (name[0] != '@' || name[1] != '{' || name[2] != '-')
return -1;
- brace = strchr(name, '}');
+ brace = memchr(name, '}', namelen);
if (!brace)
return -1;
nth = strtol(name + 3, &num_end, 10);
return -1;
/* make sure it's a single @, or @@{.*}, not @foo */
- next = strchr(name + len + 1, '@');
+ next = memchr(name + len + 1, '@', namelen - len - 1);
if (next && next[1] != '{')
return -1;
if (!next)
return ret - used + len;
}
+static void set_shortened_ref(struct strbuf *buf, const char *ref)
+{
+ char *s = shorten_unambiguous_ref(ref, 0);
+ strbuf_reset(buf);
+ strbuf_addstr(buf, s);
+ free(s);
+}
+
+static const char *get_upstream_branch(const char *branch_buf, int len)
+{
+ char *branch = xstrndup(branch_buf, len);
+ struct branch *upstream = branch_get(*branch ? branch : NULL);
+
+ /*
+ * Upstream can be NULL only if branch refers to HEAD and HEAD
+ * points to something different than a branch.
+ */
+ if (!upstream)
+ die(_("HEAD does not point to a branch"));
+ if (!upstream->merge || !upstream->merge[0]->dst) {
+ if (!ref_exists(upstream->refname))
+ die(_("No such branch: '%s'"), branch);
+ if (!upstream->merge) {
+ die(_("No upstream configured for branch '%s'"),
+ upstream->name);
+ }
+ die(
+ _("Upstream branch '%s' not stored as a remote-tracking branch"),
+ upstream->merge[0]->src);
+ }
+ free(branch);
+
+ return upstream->merge[0]->dst;
+}
+
+static int interpret_upstream_mark(const char *name, int namelen,
+ int at, struct strbuf *buf)
+{
+ int len;
+
+ len = upstream_mark(name + at, namelen - at);
+ if (!len)
+ return -1;
+
+ if (memchr(name, ':', at))
+ return -1;
+
+ set_shortened_ref(buf, get_upstream_branch(name, at));
+ return len + at;
+}
+
/*
* This reads short-hand syntax that not only evaluates to a commit
* object name, but also can act as if the end user spelled the name
*/
int interpret_branch_name(const char *name, int namelen, struct strbuf *buf)
{
- char *cp;
- struct branch *upstream;
- int len = interpret_nth_prior_checkout(name, buf);
- int tmp_len;
+ char *at;
+ const char *start;
+ int len = interpret_nth_prior_checkout(name, namelen, buf);
if (!namelen)
namelen = strlen(name);
return reinterpret(name, namelen, len, buf);
}
- cp = strchr(name, '@');
- if (!cp)
- return -1;
-
- len = interpret_empty_at(name, namelen, cp - name, buf);
- if (len > 0)
- return reinterpret(name, namelen, len, buf);
+ for (start = name;
+ (at = memchr(start, '@', namelen - (start - name)));
+ start = at + 1) {
- tmp_len = upstream_mark(cp, namelen - (cp - name));
- if (!tmp_len)
- return -1;
+ len = interpret_empty_at(name, namelen, at - name, buf);
+ if (len > 0)
+ return reinterpret(name, namelen, len, buf);
- len = cp + tmp_len - name;
- cp = xstrndup(name, cp - name);
- upstream = branch_get(*cp ? cp : NULL);
- /*
- * Upstream can be NULL only if cp refers to HEAD and HEAD
- * points to something different than a branch.
- */
- if (!upstream)
- die(_("HEAD does not point to a branch"));
- if (!upstream->merge || !upstream->merge[0]->dst) {
- if (!ref_exists(upstream->refname))
- die(_("No such branch: '%s'"), cp);
- if (!upstream->merge) {
- die(_("No upstream configured for branch '%s'"),
- upstream->name);
- }
- die(
- _("Upstream branch '%s' not stored as a remote-tracking branch"),
- upstream->merge[0]->src);
+ len = interpret_upstream_mark(name, namelen, at - name, buf);
+ if (len > 0)
+ return len;
}
- free(cp);
- cp = shorten_unambiguous_ref(upstream->merge[0]->dst, 0);
- strbuf_reset(buf);
- strbuf_addstr(buf, cp);
- free(cp);
- return len;
+
+ return -1;
}
int strbuf_branchname(struct strbuf *sb, const char *name)
static const char upload_pack_usage[] = "git upload-pack [--strict] [--timeout=<n>] <dir>";
- /* bits #0..7 in revision.h, #8..10 in commit.c */
+ /* Remember to update object flag allocation in object.h */
#define THEY_HAVE (1u << 11)
#define OUR_REF (1u << 12)
#define WANTED (1u << 13)
return sz;
}
+static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
+{
+ FILE *fp = cb_data;
+ if (graft->nr_parent == -1)
+ fprintf(fp, "--shallow %s\n", sha1_to_hex(graft->sha1));
+ return 0;
+}
+
static void create_pack_file(void)
{
struct child_process pack_objects;
const char *argv[12];
int i, arg = 0;
FILE *pipe_fd;
- char *shallow_file = NULL;
if (shallow_nr) {
- shallow_file = setup_temporary_shallow(NULL);
argv[arg++] = "--shallow-file";
- argv[arg++] = shallow_file;
+ argv[arg++] = "";
}
argv[arg++] = "pack-objects";
argv[arg++] = "--revs";
pipe_fd = xfdopen(pack_objects.in, "w");
+ if (shallow_nr)
+ for_each_commit_graft(write_one_shallow, pipe_fd);
+
for (i = 0; i < want_obj.nr; i++)
fprintf(pipe_fd, "%s\n",
sha1_to_hex(want_obj.objects[i].item->sha1));
error("git upload-pack: git-pack-objects died with error.");
goto fail;
}
- if (shallow_file) {
- if (*shallow_file)
- unlink(shallow_file);
- free(shallow_file);
- }
/* flush the data */
if (0 <= buffered) {
packet_trace_identity("upload-pack");
git_extract_argv0_path(argv[0]);
- read_replace_refs = 0;
+ check_replace_refs = 0;
for (i = 1; i < argc; i++) {
char *arg = argv[i];