summaryrefslogtreecommitdiff
path: root/builtin
diff options
context:
space:
mode:
Diffstat (limited to 'builtin')
-rw-r--r--builtin/add.c20
-rw-r--r--builtin/apply.c371
-rw-r--r--builtin/blame.c125
-rw-r--r--builtin/branch.c32
-rw-r--r--builtin/bundle.c6
-rw-r--r--builtin/cat-file.c42
-rw-r--r--builtin/check-ref-format.c44
-rw-r--r--builtin/checkout.c111
-rw-r--r--builtin/clean.c17
-rw-r--r--builtin/clone.c30
-rw-r--r--builtin/commit-tree.c70
-rw-r--r--builtin/commit.c120
-rw-r--r--builtin/config.c18
-rw-r--r--builtin/describe.c49
-rw-r--r--builtin/diff-files.c2
-rw-r--r--builtin/diff-index.c2
-rw-r--r--builtin/diff-tree.c2
-rw-r--r--builtin/diff.c20
-rw-r--r--builtin/fast-export.c56
-rw-r--r--builtin/fetch.c81
-rw-r--r--builtin/fmt-merge-msg.c118
-rw-r--r--builtin/for-each-ref.c8
-rw-r--r--builtin/grep.c140
-rw-r--r--builtin/help.c14
-rw-r--r--builtin/index-pack.c115
-rw-r--r--builtin/init-db.c2
-rw-r--r--builtin/log.c94
-rw-r--r--builtin/ls-files.c121
-rw-r--r--builtin/ls-remote.c22
-rw-r--r--builtin/mailinfo.c4
-rw-r--r--builtin/mailsplit.c4
-rw-r--r--builtin/merge-base.c44
-rw-r--r--builtin/merge-file.c6
-rw-r--r--builtin/merge-recursive.c9
-rw-r--r--builtin/merge-tree.c5
-rw-r--r--builtin/merge.c125
-rw-r--r--builtin/mv.c4
-rw-r--r--builtin/name-rev.c2
-rw-r--r--builtin/notes.c22
-rw-r--r--builtin/pack-objects.c15
-rw-r--r--builtin/patch-id.c107
-rw-r--r--builtin/prune.c5
-rw-r--r--builtin/push.c12
-rw-r--r--builtin/read-tree.c5
-rw-r--r--builtin/receive-pack.c130
-rw-r--r--builtin/reflog.c159
-rw-r--r--builtin/remote.c214
-rw-r--r--builtin/rerere.c83
-rw-r--r--builtin/reset.c2
-rw-r--r--builtin/rev-list.c16
-rw-r--r--builtin/rev-parse.c5
-rw-r--r--builtin/revert.c397
-rw-r--r--builtin/shortlog.c10
-rw-r--r--builtin/show-branch.c3
-rw-r--r--builtin/show-ref.c4
-rw-r--r--builtin/var.c9
56 files changed, 2180 insertions, 1073 deletions
diff --git a/builtin/add.c b/builtin/add.c
index 87d2980313..56a4e0af6b 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -261,12 +261,14 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
{
char *file = xstrdup(git_path("ADD_EDIT.patch"));
const char *apply_argv[] = { "apply", "--recount", "--cached",
- file, NULL };
+ NULL, NULL };
struct child_process child;
struct rev_info rev;
int out;
struct stat st;
+ apply_argv[3] = file;
+
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
if (read_cache() < 0)
@@ -308,7 +310,7 @@ static const char ignore_error[] =
"The following paths are ignored by one of your .gitignore files:\n";
static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0;
-static int ignore_add_errors, addremove, intent_to_add;
+static int ignore_add_errors, addremove, intent_to_add, ignore_missing = 0;
static struct option builtin_add_options[] = {
OPT__DRY_RUN(&show_only),
@@ -323,6 +325,7 @@ static struct option builtin_add_options[] = {
OPT_BOOLEAN('A', "all", &addremove, "add all, noticing removal of tracked files"),
OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"),
OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, "just skip files which cannot be added because of errors"),
+ OPT_BOOLEAN( 0 , "ignore-missing", &ignore_missing, "check if - even missing - files are ignored in dry run"),
OPT_END(),
};
@@ -383,6 +386,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
if (addremove && take_worktree_changes)
die("-A and -u are mutually incompatible");
+ if (!show_only && ignore_missing)
+ die("Option --ignore-missing can only be used together with --dry-run");
if ((addremove || take_worktree_changes) && !argc) {
static const char *here[2] = { ".", NULL };
argc = 1;
@@ -439,9 +444,14 @@ int cmd_add(int argc, const char **argv, const char *prefix)
seen = find_used_pathspec(pathspec);
for (i = 0; pathspec[i]; i++) {
if (!seen[i] && pathspec[i][0]
- && !file_exists(pathspec[i]))
- die("pathspec '%s' did not match any files",
- pathspec[i]);
+ && !file_exists(pathspec[i])) {
+ if (ignore_missing) {
+ if (excluded(&dir, pathspec[i], DT_UNKNOWN))
+ dir_add_ignored(&dir, pathspec[i], strlen(pathspec[i]));
+ } else
+ die("pathspec '%s' did not match any files",
+ pathspec[i]);
+ }
}
free(seen);
}
diff --git a/builtin/apply.c b/builtin/apply.c
index 771c972c55..23c18c573b 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -56,7 +56,7 @@ static enum ws_error_action {
nowarn_ws_error,
warn_on_ws_error,
die_on_ws_error,
- correct_ws_error,
+ correct_ws_error
} ws_error_action = warn_on_ws_error;
static int whitespace_error;
static int squelch_whitespace_errors = 5;
@@ -64,7 +64,7 @@ static int applied_after_fixing_ws;
static enum ws_ignore {
ignore_ws_none,
- ignore_ws_change,
+ ignore_ws_change
} ws_ignore_action = ignore_ws_none;
@@ -416,48 +416,190 @@ static char *squash_slash(char *name)
return name;
}
-static char *find_name(const char *line, char *def, int p_value, int terminate)
+static char *find_name_gnu(const char *line, char *def, int p_value)
{
- int len;
- const char *start = NULL;
+ struct strbuf name = STRBUF_INIT;
+ char *cp;
- if (p_value == 0)
- start = line;
+ /*
+ * Proposed "new-style" GNU patch/diff format; see
+ * http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2
+ */
+ if (unquote_c_style(&name, line, NULL)) {
+ strbuf_release(&name);
+ return NULL;
+ }
- if (*line == '"') {
- struct strbuf name = STRBUF_INIT;
+ for (cp = name.buf; p_value; p_value--) {
+ cp = strchr(cp, '/');
+ if (!cp) {
+ strbuf_release(&name);
+ return NULL;
+ }
+ cp++;
+ }
- /*
- * Proposed "new-style" GNU patch/diff format; see
- * http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2
- */
- if (!unquote_c_style(&name, line, NULL)) {
- char *cp;
+ /* name can later be freed, so we need
+ * to memmove, not just return cp
+ */
+ strbuf_remove(&name, 0, cp - name.buf);
+ free(def);
+ if (root)
+ strbuf_insert(&name, 0, root, root_len);
+ return squash_slash(strbuf_detach(&name, NULL));
+}
- for (cp = name.buf; p_value; p_value--) {
- cp = strchr(cp, '/');
- if (!cp)
- break;
- cp++;
- }
- if (cp) {
- /* name can later be freed, so we need
- * to memmove, not just return cp
- */
- strbuf_remove(&name, 0, cp - name.buf);
- free(def);
- if (root)
- strbuf_insert(&name, 0, root, root_len);
- return squash_slash(strbuf_detach(&name, NULL));
- }
- }
- strbuf_release(&name);
+static size_t tz_len(const char *line, size_t len)
+{
+ const char *tz, *p;
+
+ if (len < strlen(" +0500") || line[len-strlen(" +0500")] != ' ')
+ return 0;
+ tz = line + len - strlen(" +0500");
+
+ if (tz[1] != '+' && tz[1] != '-')
+ return 0;
+
+ for (p = tz + 2; p != line + len; p++)
+ if (!isdigit(*p))
+ return 0;
+
+ return line + len - tz;
+}
+
+static size_t date_len(const char *line, size_t len)
+{
+ const char *date, *p;
+
+ if (len < strlen("72-02-05") || line[len-strlen("-05")] != '-')
+ return 0;
+ p = date = line + len - strlen("72-02-05");
+
+ if (!isdigit(*p++) || !isdigit(*p++) || *p++ != '-' ||
+ !isdigit(*p++) || !isdigit(*p++) || *p++ != '-' ||
+ !isdigit(*p++) || !isdigit(*p++)) /* Not a date. */
+ return 0;
+
+ if (date - line >= strlen("19") &&
+ isdigit(date[-1]) && isdigit(date[-2])) /* 4-digit year */
+ date -= strlen("19");
+
+ return line + len - date;
+}
+
+static size_t short_time_len(const char *line, size_t len)
+{
+ const char *time, *p;
+
+ if (len < strlen(" 07:01:32") || line[len-strlen(":32")] != ':')
+ return 0;
+ p = time = line + len - strlen(" 07:01:32");
+
+ /* Permit 1-digit hours? */
+ if (*p++ != ' ' ||
+ !isdigit(*p++) || !isdigit(*p++) || *p++ != ':' ||
+ !isdigit(*p++) || !isdigit(*p++) || *p++ != ':' ||
+ !isdigit(*p++) || !isdigit(*p++)) /* Not a time. */
+ return 0;
+
+ return line + len - time;
+}
+
+static size_t fractional_time_len(const char *line, size_t len)
+{
+ const char *p;
+ size_t n;
+
+ /* Expected format: 19:41:17.620000023 */
+ if (!len || !isdigit(line[len - 1]))
+ return 0;
+ p = line + len - 1;
+
+ /* Fractional seconds. */
+ while (p > line && isdigit(*p))
+ p--;
+ if (*p != '.')
+ return 0;
+
+ /* Hours, minutes, and whole seconds. */
+ n = short_time_len(line, p - line);
+ if (!n)
+ return 0;
+
+ return line + len - p + n;
+}
+
+static size_t trailing_spaces_len(const char *line, size_t len)
+{
+ const char *p;
+
+ /* Expected format: ' ' x (1 or more) */
+ if (!len || line[len - 1] != ' ')
+ return 0;
+
+ p = line + len;
+ while (p != line) {
+ p--;
+ if (*p != ' ')
+ return line + len - (p + 1);
}
- for (;;) {
+ /* All spaces! */
+ return len;
+}
+
+static size_t diff_timestamp_len(const char *line, size_t len)
+{
+ const char *end = line + len;
+ size_t n;
+
+ /*
+ * Posix: 2010-07-05 19:41:17
+ * GNU: 2010-07-05 19:41:17.620000023 -0500
+ */
+
+ if (!isdigit(end[-1]))
+ return 0;
+
+ n = tz_len(line, end - line);
+ end -= n;
+
+ n = short_time_len(line, end - line);
+ if (!n)
+ n = fractional_time_len(line, end - line);
+ end -= n;
+
+ n = date_len(line, end - line);
+ if (!n) /* No date. Too bad. */
+ return 0;
+ end -= n;
+
+ if (end == line) /* No space before date. */
+ return 0;
+ if (end[-1] == '\t') { /* Success! */
+ end--;
+ return line + len - end;
+ }
+ if (end[-1] != ' ') /* No space before date. */
+ return 0;
+
+ /* Whitespace damage. */
+ end -= trailing_spaces_len(line, end - line);
+ return line + len - end;
+}
+
+static char *find_name_common(const char *line, char *def, int p_value,
+ const char *end, int terminate)
+{
+ int len;
+ const char *start = NULL;
+
+ if (p_value == 0)
+ start = line;
+ while (line != end) {
char c = *line;
- if (isspace(c)) {
+ if (!end && isspace(c)) {
if (c == '\n')
break;
if (name_terminate(start, line-start, c, terminate))
@@ -497,6 +639,37 @@ static char *find_name(const char *line, char *def, int p_value, int terminate)
return squash_slash(xmemdupz(start, len));
}
+static char *find_name(const char *line, char *def, int p_value, int terminate)
+{
+ if (*line == '"') {
+ char *name = find_name_gnu(line, def, p_value);
+ if (name)
+ return name;
+ }
+
+ return find_name_common(line, def, p_value, NULL, terminate);
+}
+
+static char *find_name_traditional(const char *line, char *def, int p_value)
+{
+ size_t len = strlen(line);
+ size_t date_len;
+
+ if (*line == '"') {
+ char *name = find_name_gnu(line, def, p_value);
+ if (name)
+ return name;
+ }
+
+ len = strchrnul(line, '\n') - line;
+ date_len = diff_timestamp_len(line, len);
+ if (!date_len)
+ return find_name_common(line, def, p_value, NULL, TERM_TAB);
+ len -= date_len;
+
+ return find_name_common(line, def, p_value, line + len, 0);
+}
+
static int count_slashes(const char *cp)
{
int cnt = 0;
@@ -519,7 +692,7 @@ static int guess_p_value(const char *nameline)
if (is_dev_null(nameline))
return -1;
- name = find_name(nameline, NULL, 0, TERM_SPACE | TERM_TAB);
+ name = find_name_traditional(nameline, NULL, 0);
if (!name)
return -1;
cp = strchr(name, '/');
@@ -638,16 +811,16 @@ static void parse_traditional_patch(const char *first, const char *second, struc
if (is_dev_null(first)) {
patch->is_new = 1;
patch->is_delete = 0;
- name = find_name(second, NULL, p_value, TERM_SPACE | TERM_TAB);
+ name = find_name_traditional(second, NULL, p_value);
patch->new_name = name;
} else if (is_dev_null(second)) {
patch->is_new = 0;
patch->is_delete = 1;
- name = find_name(first, NULL, p_value, TERM_SPACE | TERM_TAB);
+ name = find_name_traditional(first, NULL, p_value);
patch->old_name = name;
} else {
- name = find_name(first, NULL, p_value, TERM_SPACE | TERM_TAB);
- name = find_name(second, name, p_value, TERM_SPACE | TERM_TAB);
+ name = find_name_traditional(first, NULL, p_value);
+ name = find_name_traditional(second, name, p_value);
if (has_epoch_timestamp(first)) {
patch->is_new = 1;
patch->is_delete = 0;
@@ -1854,6 +2027,8 @@ static int match_fragment(struct image *img,
{
int i;
char *fixed_buf, *buf, *orig, *target;
+ struct strbuf fixed;
+ size_t fixed_len;
int preimage_limit;
if (preimage->nr + try_lno <= img->nr) {
@@ -1864,13 +2039,13 @@ static int match_fragment(struct image *img,
if (match_end && (preimage->nr + try_lno != img->nr))
return 0;
} else if (ws_error_action == correct_ws_error &&
- (ws_rule & WS_BLANK_AT_EOF) && match_end) {
+ (ws_rule & WS_BLANK_AT_EOF)) {
/*
- * This hunk that matches at the end extends beyond
- * the end of img, and we are removing blank lines
- * at the end of the file. This many lines from the
- * beginning of the preimage must match with img, and
- * the remainder of the preimage must be blank.
+ * This hunk extends beyond the end of img, and we are
+ * removing blank lines at the end of the file. This
+ * many lines from the beginning of the preimage must
+ * match with img, and the remainder of the preimage
+ * must be blank.
*/
preimage_limit = img->nr - try_lno;
} else {
@@ -1977,12 +2152,12 @@ static int match_fragment(struct image *img,
* use the whitespace from the preimage.
*/
extra_chars = preimage_end - preimage_eof;
- fixed_buf = xmalloc(imgoff + extra_chars);
- memcpy(fixed_buf, img->buf + try, imgoff);
- memcpy(fixed_buf + imgoff, preimage_eof, extra_chars);
- imgoff += extra_chars;
+ strbuf_init(&fixed, imgoff + extra_chars);
+ strbuf_add(&fixed, img->buf + try, imgoff);
+ strbuf_add(&fixed, preimage_eof, extra_chars);
+ fixed_buf = strbuf_detach(&fixed, &fixed_len);
update_pre_post_images(preimage, postimage,
- fixed_buf, imgoff, postlen);
+ fixed_buf, fixed_len, postlen);
return 1;
}
@@ -1999,27 +2174,22 @@ static int match_fragment(struct image *img,
* but in this loop we will only handle the part of the
* preimage that falls within the file.
*/
- fixed_buf = xmalloc(preimage->len + 1);
- buf = fixed_buf;
+ strbuf_init(&fixed, preimage->len + 1);
orig = preimage->buf;
target = img->buf + try;
for (i = 0; i < preimage_limit; i++) {
- size_t fixlen; /* length after fixing the preimage */
size_t oldlen = preimage->line[i].len;
size_t tgtlen = img->line[try_lno + i].len;
- size_t tgtfixlen; /* length after fixing the target line */
- char tgtfixbuf[1024], *tgtfix;
+ size_t fixstart = fixed.len;
+ struct strbuf tgtfix;
int match;
/* Try fixing the line in the preimage */
- fixlen = ws_fix_copy(buf, orig, oldlen, ws_rule, NULL);
+ ws_fix_copy(&fixed, orig, oldlen, ws_rule, NULL);
/* Try fixing the line in the target */
- if (sizeof(tgtfixbuf) > tgtlen)
- tgtfix = tgtfixbuf;
- else
- tgtfix = xmalloc(tgtlen);
- tgtfixlen = ws_fix_copy(tgtfix, target, tgtlen, ws_rule, NULL);
+ strbuf_init(&tgtfix, tgtlen);
+ ws_fix_copy(&tgtfix, target, tgtlen, ws_rule, NULL);
/*
* If they match, either the preimage was based on
@@ -2031,15 +2201,15 @@ static int match_fragment(struct image *img,
* so we might as well take the fix together with their
* real change.
*/
- match = (tgtfixlen == fixlen && !memcmp(tgtfix, buf, fixlen));
+ match = (tgtfix.len == fixed.len - fixstart &&
+ !memcmp(tgtfix.buf, fixed.buf + fixstart,
+ fixed.len - fixstart));
- if (tgtfix != tgtfixbuf)
- free(tgtfix);
+ strbuf_release(&tgtfix);
if (!match)
goto unmatch_exit;
orig += oldlen;
- buf += fixlen;
target += tgtlen;
}
@@ -2051,19 +2221,18 @@ static int match_fragment(struct image *img,
* false).
*/
for ( ; i < preimage->nr; i++) {
- size_t fixlen; /* length after fixing the preimage */
+ size_t fixstart = fixed.len; /* start of the fixed preimage */
size_t oldlen = preimage->line[i].len;
int j;
/* Try fixing the line in the preimage */
- fixlen = ws_fix_copy(buf, orig, oldlen, ws_rule, NULL);
+ ws_fix_copy(&fixed, orig, oldlen, ws_rule, NULL);
- for (j = 0; j < fixlen; j++)
- if (!isspace(buf[j]))
+ for (j = fixstart; j < fixed.len; j++)
+ if (!isspace(fixed.buf[j]))
goto unmatch_exit;
orig += oldlen;
- buf += fixlen;
}
/*
@@ -2071,12 +2240,13 @@ static int match_fragment(struct image *img,
* has whitespace breakages unfixed, and fixing them makes the
* hunk match. Update the context lines in the postimage.
*/
+ fixed_buf = strbuf_detach(&fixed, &fixed_len);
update_pre_post_images(preimage, postimage,
- fixed_buf, buf - fixed_buf, 0);
+ fixed_buf, fixed_len, 0);
return 1;
unmatch_exit:
- free(fixed_buf);
+ strbuf_release(&fixed);
return 0;
}
@@ -2244,7 +2414,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
int match_beginning, match_end;
const char *patch = frag->patch;
int size = frag->size;
- char *old, *new, *oldlines, *newlines;
+ char *old, *oldlines;
+ struct strbuf newlines;
int new_blank_lines_at_end = 0;
unsigned long leading, trailing;
int pos, applied_pos;
@@ -2254,16 +2425,16 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
memset(&preimage, 0, sizeof(preimage));
memset(&postimage, 0, sizeof(postimage));
oldlines = xmalloc(size);
- newlines = xmalloc(size);
+ strbuf_init(&newlines, size);
old = oldlines;
- new = newlines;
while (size > 0) {
char first;
int len = linelen(patch, size);
- int plen, added;
+ int plen;
int added_blank_line = 0;
int is_blank_context = 0;
+ size_t start;
if (!len)
break;
@@ -2293,7 +2464,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
/* ... followed by '\No newline'; nothing */
break;
*old++ = '\n';
- *new++ = '\n';
+ strbuf_addch(&newlines, '\n');
add_line_info(&preimage, "\n", 1, LINE_COMMON);
add_line_info(&postimage, "\n", 1, LINE_COMMON);
is_blank_context = 1;
@@ -2315,18 +2486,17 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
if (first == '+' && no_add)
break;
+ start = newlines.len;
if (first != '+' ||
!whitespace_error ||
ws_error_action != correct_ws_error) {
- memcpy(new, patch + 1, plen);
- added = plen;
+ strbuf_add(&newlines, patch + 1, plen);
}
else {
- added = ws_fix_copy(new, patch + 1, plen, ws_rule, &applied_after_fixing_ws);
+ ws_fix_copy(&newlines, patch + 1, plen, ws_rule, &applied_after_fixing_ws);
}
- add_line_info(&postimage, new, added,
+ add_line_info(&postimage, newlines.buf + start, newlines.len - start,
(first == '+' ? 0 : LINE_COMMON));
- new += added;
if (first == '+' &&
(ws_rule & WS_BLANK_AT_EOF) &&
ws_blank_line(patch + 1, plen, ws_rule))
@@ -2351,9 +2521,9 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
}
if (inaccurate_eof &&
old > oldlines && old[-1] == '\n' &&
- new > newlines && new[-1] == '\n') {
+ newlines.len > 0 && newlines.buf[newlines.len - 1] == '\n') {
old--;
- new--;
+ strbuf_setlen(&newlines, newlines.len - 1);
}
leading = frag->leading;
@@ -2385,8 +2555,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
pos = frag->newpos ? (frag->newpos - 1) : 0;
preimage.buf = oldlines;
preimage.len = old - oldlines;
- postimage.buf = newlines;
- postimage.len = new - newlines;
+ postimage.buf = newlines.buf;
+ postimage.len = newlines.len;
preimage.line = preimage.line_allocated;
postimage.line = postimage.line_allocated;
@@ -2462,7 +2632,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
}
free(oldlines);
- free(newlines);
+ strbuf_release(&newlines);
free(preimage.line_allocated);
free(postimage.line_allocated);
@@ -2631,7 +2801,7 @@ static struct patch *in_fn_table(const char *name)
if (name == NULL)
return NULL;
- item = string_list_lookup(name, &fn_table);
+ item = string_list_lookup(&fn_table, name);
if (item != NULL)
return (struct patch *)item->util;
@@ -2667,7 +2837,7 @@ static void add_to_fn_table(struct patch *patch)
* file creations and copies
*/
if (patch->new_name != NULL) {
- item = string_list_insert(patch->new_name, &fn_table);
+ item = string_list_insert(&fn_table, patch->new_name);
item->util = patch;
}
@@ -2676,7 +2846,7 @@ static void add_to_fn_table(struct patch *patch)
* later chunks shouldn't patch old names
*/
if ((patch->new_name == NULL) || (patch->is_rename)) {
- item = string_list_insert(patch->old_name, &fn_table);
+ item = string_list_insert(&fn_table, patch->old_name);
item->util = PATH_WAS_DELETED;
}
}
@@ -2689,7 +2859,7 @@ static void prepare_fn_table(struct patch *patch)
while (patch) {
if ((patch->new_name == NULL) || (patch->is_rename)) {
struct string_list_item *item;
- item = string_list_insert(patch->old_name, &fn_table);
+ item = string_list_insert(&fn_table, patch->old_name);
item->util = PATH_TO_BE_DELETED;
}
patch = patch->next;
@@ -2982,8 +3152,7 @@ static void build_fake_ancestor(struct patch *list, const char *filename)
else if (get_sha1(patch->old_sha1_prefix, sha1))
/* git diff has no index line for mode/type changes */
if (!patch->lines_added && !patch->lines_deleted) {
- if (get_current_sha1(patch->new_name, sha1) ||
- get_current_sha1(patch->old_name, sha1))
+ if (get_current_sha1(patch->old_name, sha1))
die("mode change for %s, which is not "
"in current HEAD", name);
sha1_ptr = sha1;
@@ -3141,11 +3310,7 @@ static void remove_file(struct patch *patch, int rmdir_empty)
die("unable to remove %s from index", patch->old_name);
}
if (!cached) {
- if (S_ISGITLINK(patch->old_mode)) {
- if (rmdir(patch->old_name))
- warning("unable to remove submodule %s",
- patch->old_name);
- } else if (!unlink_or_warn(patch->old_name) && rmdir_empty) {
+ if (!remove_or_warn(patch->old_mode, patch->old_name) && rmdir_empty) {
remove_path(patch->old_name);
}
}
@@ -3401,7 +3566,7 @@ static void add_name_limit(const char *name, int exclude)
{
struct string_list_item *it;
- it = string_list_append(name, &limit_by_name);
+ it = string_list_append(&limit_by_name, name);
it->util = exclude ? NULL : (void *) 1;
}
@@ -3614,11 +3779,11 @@ static int option_parse_directory(const struct option *opt,
return 0;
}
-int cmd_apply(int argc, const char **argv, const char *unused_prefix)
+int cmd_apply(int argc, const char **argv, const char *prefix_)
{
int i;
int errs = 0;
- int is_not_gitdir;
+ int is_not_gitdir = !startup_info->have_repository;
int binary;
int force_apply = 0;
@@ -3691,7 +3856,7 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
OPT_END()
};
- prefix = setup_git_directory_gently(&is_not_gitdir);
+ prefix = prefix_;
prefix_length = prefix ? strlen(prefix) : 0;
git_config(git_apply_config, NULL);
if (apply_default_whitespace)
diff --git a/builtin/blame.c b/builtin/blame.c
index fc1586350f..101535448f 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -20,6 +20,7 @@
#include "mailmap.h"
#include "parse-options.h"
#include "utf8.h"
+#include "userdiff.h"
static char blame_usage[] = "git blame [options] [rev-opts] [rev] [--] file";
@@ -39,7 +40,7 @@ static int show_root;
static int reverse;
static int blank_boundary;
static int incremental;
-static int xdl_opts = XDF_NEED_MINIMAL;
+static int xdl_opts;
static enum date_mode blame_date_mode = DATE_ISO8601;
static size_t blame_date_width;
@@ -86,16 +87,50 @@ struct origin {
};
/*
+ * Prepare diff_filespec and convert it using diff textconv API
+ * if the textconv driver exists.
+ * Return 1 if the conversion succeeds, 0 otherwise.
+ */
+int textconv_object(const char *path,
+ const unsigned char *sha1,
+ char **buf,
+ unsigned long *buf_size)
+{
+ struct diff_filespec *df;
+ struct userdiff_driver *textconv;
+
+ df = alloc_filespec(path);
+ fill_filespec(df, sha1, S_IFREG | 0664);
+ textconv = get_textconv(df);
+ if (!textconv) {
+ free_filespec(df);
+ return 0;
+ }
+
+ *buf_size = fill_textconv(textconv, df, buf);
+ free_filespec(df);
+ return 1;
+}
+
+/*
* Given an origin, prepare mmfile_t structure to be used by the
* diff machinery
*/
-static void fill_origin_blob(struct origin *o, mmfile_t *file)
+static void fill_origin_blob(struct diff_options *opt,
+ struct origin *o, mmfile_t *file)
{
if (!o->file.ptr) {
enum object_type type;
+ unsigned long file_size;
+
num_read_blob++;
- file->ptr = read_sha1_file(o->blob_sha1, &type,
- (unsigned long *)(&(file->size)));
+ if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
+ textconv_object(o->path, o->blob_sha1, &file->ptr, &file_size))
+ ;
+ else
+ file->ptr = read_sha1_file(o->blob_sha1, &type, &file_size);
+ file->size = file_size;
+
if (!file->ptr)
die("Cannot read blob %s for path %s",
sha1_to_hex(o->blob_sha1),
@@ -282,7 +317,6 @@ static struct origin *get_origin(struct scoreboard *sb,
static int fill_blob_sha1(struct origin *origin)
{
unsigned mode;
-
if (!is_null_sha1(origin->blob_sha1))
return 0;
if (get_tree_entry(origin->commit->object.sha1,
@@ -733,16 +767,17 @@ static int pass_blame_to_parent(struct scoreboard *sb,
{
int last_in_target;
mmfile_t file_p, file_o;
- struct blame_chunk_cb_data d = { sb, target, parent, 0, 0 };
+ struct blame_chunk_cb_data d;
xpparam_t xpp;
xdemitconf_t xecfg;
-
+ memset(&d, 0, sizeof(d));
+ d.sb = sb; d.target = target; d.parent = parent;
last_in_target = find_last_in_target(sb, target);
if (last_in_target < 0)
return 1; /* nothing remains for this target */
- fill_origin_blob(parent, &file_p);
- fill_origin_blob(target, &file_o);
+ fill_origin_blob(&sb->revs->diffopt, parent, &file_p);
+ fill_origin_blob(&sb->revs->diffopt, target, &file_o);
num_get_patch++;
memset(&xpp, 0, sizeof(xpp));
@@ -875,10 +910,11 @@ static void find_copy_in_blob(struct scoreboard *sb,
const char *cp;
int cnt;
mmfile_t file_o;
- struct handle_split_cb_data d = { sb, ent, parent, split, 0, 0 };
+ struct handle_split_cb_data d;
xpparam_t xpp;
xdemitconf_t xecfg;
-
+ memset(&d, 0, sizeof(d));
+ d.sb = sb; d.ent = ent; d.parent = parent; d.split = split;
/*
* Prepare mmfile that contains only the lines in ent.
*/
@@ -922,7 +958,7 @@ static int find_move_in_parent(struct scoreboard *sb,
if (last_in_target < 0)
return 1; /* nothing remains for this target */
- fill_origin_blob(parent, &file_p);
+ fill_origin_blob(&sb->revs->diffopt, parent, &file_p);
if (!file_p.ptr)
return 0;
@@ -1063,7 +1099,7 @@ static int find_copy_in_parent(struct scoreboard *sb,
norigin = get_origin(sb, parent, p->one->path);
hashcpy(norigin->blob_sha1, p->one->sha1);
- fill_origin_blob(norigin, &file_p);
+ fill_origin_blob(&sb->revs->diffopt, norigin, &file_p);
if (!file_p.ptr)
continue;
@@ -1371,7 +1407,8 @@ static void get_commit_info(struct commit *commit,
int detailed)
{
int len;
- char *tmp, *endp, *reencoded, *message;
+ const char *subject;
+ char *reencoded, *message;
static char author_name[1024];
static char author_mail[1024];
static char committer_name[1024];
@@ -1413,22 +1450,13 @@ static void get_commit_info(struct commit *commit,
&ret->committer_time, &ret->committer_tz);
ret->summary = summary_buf;
- tmp = strstr(message, "\n\n");
- if (!tmp) {
- error_out:
+ len = find_commit_subject(message, &subject);
+ if (len && len < sizeof(summary_buf)) {
+ memcpy(summary_buf, subject, len);
+ summary_buf[len] = 0;
+ } else {
sprintf(summary_buf, "(%s)", sha1_to_hex(commit->object.sha1));
- free(reencoded);
- return;
}
- tmp += 2;
- endp = strchr(tmp, '\n');
- if (!endp)
- endp = tmp + strlen(tmp);
- len = endp - tmp;
- if (len >= sizeof(summary_buf) || len == 0)
- goto error_out;
- memcpy(summary_buf, tmp, len);
- summary_buf[len] = 0;
free(reencoded);
}
@@ -1589,7 +1617,7 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent)
strcpy(hex, sha1_to_hex(suspect->commit->object.sha1));
printf("%s%c%d %d %d\n",
hex,
- ent->guilty ? ' ' : '*', // purely for debugging
+ ent->guilty ? ' ' : '*', /* purely for debugging */
ent->s_lno + 1,
ent->lno + 1,
ent->num_lines);
@@ -1983,6 +2011,16 @@ static int git_blame_config(const char *var, const char *value, void *cb)
blame_date_mode = parse_date_format(value);
return 0;
}
+
+ switch (userdiff_config(var, value)) {
+ case 0:
+ break;
+ case -1:
+ return -1;
+ default:
+ return 0;
+ }
+
return git_default_config(var, value, cb);
}
@@ -1990,7 +2028,9 @@ static int git_blame_config(const char *var, const char *value, void *cb)
* Prepare a dummy commit that represents the work tree (or staged) item.
* Note that annotating work tree item never works in the reverse.
*/
-static struct commit *fake_working_tree_commit(const char *path, const char *contents_from)
+static struct commit *fake_working_tree_commit(struct diff_options *opt,
+ const char *path,
+ const char *contents_from)
{
struct commit *commit;
struct origin *origin;
@@ -2018,6 +2058,7 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
if (!contents_from || strcmp("-", contents_from)) {
struct stat st;
const char *read_from;
+ unsigned long buf_len;
if (contents_from) {
if (stat(contents_from, &st) < 0)
@@ -2030,9 +2071,13 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
read_from = path;
}
mode = canon_mode(st.st_mode);
+
switch (st.st_mode & S_IFMT) {
case S_IFREG:
- if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
+ if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
+ textconv_object(read_from, null_sha1, &buf.buf, &buf_len))
+ buf.len = buf_len;
+ else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
die_errno("cannot open or read '%s'", read_from);
break;
case S_IFLNK:
@@ -2248,6 +2293,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
git_config(git_blame_config, NULL);
init_revisions(&revs, NULL);
revs.date_mode = blame_date_mode;
+ DIFF_OPT_SET(&revs.diffopt, ALLOW_TEXTCONV);
save_commit_buffer = 0;
dashdash_pos = 0;
@@ -2322,11 +2368,11 @@ parse_done:
*
* The remaining are:
*
- * (1) if dashdash_pos != 0, its either
+ * (1) if dashdash_pos != 0, it is either
* "blame [revisions] -- <path>" or
* "blame -- <path> <rev>"
*
- * (2) otherwise, its one of the two:
+ * (2) otherwise, it is one of the two:
* "blame [revisions] <path>"
* "blame <path> <rev>"
*
@@ -2384,7 +2430,8 @@ parse_done:
* or "--contents".
*/
setup_work_tree();
- sb.final = fake_working_tree_commit(path, contents_from);
+ sb.final = fake_working_tree_commit(&sb.revs->diffopt,
+ path, contents_from);
add_pending_object(&revs, &(sb.final->object), ":");
}
else if (contents_from)
@@ -2411,8 +2458,14 @@ parse_done:
if (fill_blob_sha1(o))
die("no such path %s in %s", path, final_commit_name);
- sb.final_buf = read_sha1_file(o->blob_sha1, &type,
- &sb.final_buf_size);
+ if (DIFF_OPT_TST(&sb.revs->diffopt, ALLOW_TEXTCONV) &&
+ textconv_object(path, o->blob_sha1, (char **) &sb.final_buf,
+ &sb.final_buf_size))
+ ;
+ else
+ sb.final_buf = read_sha1_file(o->blob_sha1, &type,
+ &sb.final_buf_size);
+
if (!sb.final_buf)
die("Cannot read blob %s for path %s",
sha1_to_hex(o->blob_sha1),
diff --git a/builtin/branch.c b/builtin/branch.c
index 6cf7e721e6..87976f0921 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -43,13 +43,13 @@ enum color_branch {
BRANCH_COLOR_PLAIN = 1,
BRANCH_COLOR_REMOTE = 2,
BRANCH_COLOR_LOCAL = 3,
- BRANCH_COLOR_CURRENT = 4,
+ BRANCH_COLOR_CURRENT = 4
};
static enum merge_filter {
NO_FILTER = 0,
SHOW_NOT_MERGED,
- SHOW_MERGED,
+ SHOW_MERGED
} merge_filter;
static unsigned char merge_filter_ref[20];
@@ -257,9 +257,15 @@ static char *resolve_symref(const char *src, const char *prefix)
return xstrdup(dst);
}
+struct append_ref_cb {
+ struct ref_list *ref_list;
+ int ret;
+};
+
static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
{
- struct ref_list *ref_list = (struct ref_list*)(cb_data);
+ struct append_ref_cb *cb = (struct append_ref_cb *)(cb_data);
+ struct ref_list *ref_list = cb->ref_list;
struct ref_item *newitem;
struct commit *commit;
int kind, i;
@@ -293,8 +299,10 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
commit = NULL;
if (ref_list->verbose || ref_list->with_commit || merge_filter != NO_FILTER) {
commit = lookup_commit_reference_gently(sha1, 1);
- if (!commit)
- return error("branch '%s' does not point at a commit", refname);
+ if (!commit) {
+ cb->ret = error("branch '%s' does not point at a commit", refname);
+ return 0;
+ }
/* Filter with with_commit if specified */
if (!is_descendant_of(commit, ref_list->with_commit))
@@ -484,9 +492,10 @@ static void show_detached(struct ref_list *ref_list)
}
}
-static void print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit)
+static int print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit)
{
int i;
+ struct append_ref_cb cb;
struct ref_list ref_list;
memset(&ref_list, 0, sizeof(ref_list));
@@ -496,7 +505,9 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str
ref_list.with_commit = with_commit;
if (merge_filter != NO_FILTER)
init_revisions(&ref_list.revs, NULL);
- for_each_rawref(append_ref, &ref_list);
+ cb.ref_list = &ref_list;
+ cb.ret = 0;
+ for_each_rawref(append_ref, &cb);
if (merge_filter != NO_FILTER) {
struct commit *filter;
filter = lookup_commit_reference_gently(merge_filter_ref, 0);
@@ -527,6 +538,11 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str
}
free_ref_list(&ref_list);
+
+ if (cb.ret)
+ error("some refs could not be read");
+
+ return cb.ret;
}
static void rename_branch(const char *oldname, const char *newname, int force)
@@ -679,7 +695,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (delete)
return delete_branches(argc, argv, delete > 1, kinds);
else if (argc == 0)
- print_ref_list(kinds, detached, verbose, abbrev, with_commit);
+ return print_ref_list(kinds, detached, verbose, abbrev, with_commit);
else if (rename && (argc == 1))
rename_branch(head, argv[0], rename > 1);
else if (rename && (argc == 2))
diff --git a/builtin/bundle.c b/builtin/bundle.c
index 2006cc5cd5..80649ba0b2 100644
--- a/builtin/bundle.c
+++ b/builtin/bundle.c
@@ -18,7 +18,6 @@ static const char builtin_bundle_usage[] =
int cmd_bundle(int argc, const char **argv, const char *prefix)
{
struct bundle_header header;
- int nongit;
const char *cmd, *bundle_file;
int bundle_fd = -1;
char buffer[PATH_MAX];
@@ -31,7 +30,6 @@ int cmd_bundle(int argc, const char **argv, const char *prefix)
argc -= 2;
argv += 2;
- prefix = setup_git_directory_gently(&nongit);
if (prefix && bundle_file[0] != '/') {
snprintf(buffer, sizeof(buffer), "%s/%s", prefix, bundle_file);
bundle_file = buffer;
@@ -54,11 +52,11 @@ int cmd_bundle(int argc, const char **argv, const char *prefix)
return !!list_bundle_refs(&header, argc, argv);
}
if (!strcmp(cmd, "create")) {
- if (nongit)
+ if (!startup_info->have_repository)
die("Need a repository to create a bundle.");
return !!create_bundle(&header, bundle_file, argc, argv);
} else if (!strcmp(cmd, "unbundle")) {
- if (nongit)
+ if (!startup_info->have_repository)
die("Need a repository to unbundle.");
return !!unbundle(&header, bundle_fd) ||
list_bundle_refs(&header, argc, argv);
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index a933eaa043..76ec3fec92 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -9,6 +9,8 @@
#include "tree.h"
#include "builtin.h"
#include "parse-options.h"
+#include "diff.h"
+#include "userdiff.h"
#define BATCH 1
#define BATCH_CHECK 2
@@ -84,10 +86,11 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
{
unsigned char sha1[20];
enum object_type type;
- void *buf;
+ char *buf;
unsigned long size;
+ struct object_context obj_context;
- if (get_sha1(obj_name, sha1))
+ if (get_sha1_with_context(obj_name, sha1, &obj_context))
die("Not a valid object name %s", obj_name);
buf = NULL;
@@ -118,7 +121,9 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
/* custom pretty-print here */
if (type == OBJ_TREE) {
- const char *ls_args[3] = {"ls-tree", obj_name, NULL};
+ const char *ls_args[3] = { NULL };
+ ls_args[0] = "ls-tree";
+ ls_args[1] = obj_name;
return cmd_ls_tree(2, ls_args, NULL);
}
@@ -132,6 +137,17 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
/* otherwise just spit out the data */
break;
+
+ case 'c':
+ if (!obj_context.path[0])
+ die("git cat-file --textconv %s: <object> must be <sha1:path>",
+ obj_name);
+
+ if (!textconv_object(obj_context.path, sha1, &buf, &size))
+ die("git cat-file --textconv: unable to run textconv on %s",
+ obj_name);
+ break;
+
case 0:
buf = read_object_with_reference(sha1, exp_type, &size, NULL);
break;
@@ -201,11 +217,25 @@ static int batch_objects(int print_contents)
}
static const char * const cat_file_usage[] = {
- "git cat-file (-t|-s|-e|-p|<type>) <object>",
+ "git cat-file (-t|-s|-e|-p|<type>|--textconv) <object>",
"git cat-file (--batch|--batch-check) < <list_of_objects>",
NULL
};
+static int git_cat_file_config(const char *var, const char *value, void *cb)
+{
+ switch (userdiff_config(var, value)) {
+ case 0:
+ break;
+ case -1:
+ return -1;
+ default:
+ return 0;
+ }
+
+ return git_default_config(var, value, cb);
+}
+
int cmd_cat_file(int argc, const char **argv, const char *prefix)
{
int opt = 0, batch = 0;
@@ -218,6 +248,8 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
OPT_SET_INT('e', NULL, &opt,
"exit with zero when there's no error", 'e'),
OPT_SET_INT('p', NULL, &opt, "pretty-print object's content", 'p'),
+ OPT_SET_INT(0, "textconv", &opt,
+ "for blob objects, run textconv on object's content", 'c'),
OPT_SET_INT(0, "batch", &batch,
"show info and content of objects fed from the standard input",
BATCH),
@@ -227,7 +259,7 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
OPT_END()
};
- git_config(git_default_config, NULL);
+ git_config(git_cat_file_config, NULL);
if (argc != 3 && argc != 2)
usage_with_options(cat_file_usage, options);
diff --git a/builtin/check-ref-format.c b/builtin/check-ref-format.c
index b106c65d80..ae3f28115a 100644
--- a/builtin/check-ref-format.c
+++ b/builtin/check-ref-format.c
@@ -33,28 +33,38 @@ static void collapse_slashes(char *dst, const char *src)
*dst = '\0';
}
+static int check_ref_format_branch(const char *arg)
+{
+ struct strbuf sb = STRBUF_INIT;
+ int nongit;
+
+ setup_git_directory_gently(&nongit);
+ if (strbuf_check_branch_ref(&sb, arg))
+ die("'%s' is not a valid branch name", arg);
+ printf("%s\n", sb.buf + 11);
+ return 0;
+}
+
+static int check_ref_format_print(const char *arg)
+{
+ char *refname = xmalloc(strlen(arg) + 1);
+
+ if (check_ref_format(arg))
+ return 1;
+ collapse_slashes(refname, arg);
+ printf("%s\n", refname);
+ return 0;
+}
+
int cmd_check_ref_format(int argc, const char **argv, const char *prefix)
{
if (argc == 2 && !strcmp(argv[1], "-h"))
usage(builtin_check_ref_format_usage);
- if (argc == 3 && !strcmp(argv[1], "--branch")) {
- struct strbuf sb = STRBUF_INIT;
-
- if (strbuf_check_branch_ref(&sb, argv[2]))
- die("'%s' is not a valid branch name", argv[2]);
- printf("%s\n", sb.buf + 11);
- exit(0);
- }
- if (argc == 3 && !strcmp(argv[1], "--print")) {
- char *refname = xmalloc(strlen(argv[2]) + 1);
-
- if (check_ref_format(argv[2]))
- exit(1);
- collapse_slashes(refname, argv[2]);
- printf("%s\n", refname);
- exit(0);
- }
+ if (argc == 3 && !strcmp(argv[1], "--branch"))
+ return check_ref_format_branch(argv[2]);
+ if (argc == 3 && !strcmp(argv[1], "--print"))
+ return check_ref_format_print(argv[2]);
if (argc != 2)
usage(builtin_check_ref_format_usage);
return !!check_ref_format(argv[1]);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 88b1f43e05..560eae1715 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -18,6 +18,7 @@
#include "xdiff-interface.h"
#include "ll-merge.h"
#include "resolve-undo.h"
+#include "submodule.h"
static const char * const checkout_usage[] = {
"git checkout [options] <branch>",
@@ -32,9 +33,15 @@ struct checkout_opts {
int writeout_stage;
int writeout_error;
+ /* not set by parse_options */
+ int branch_exists;
+
const char *new_branch;
+ const char *new_branch_force;
+ const char *new_orphan_branch;
int new_branch_log;
enum branch_track track;
+ struct diff_options diff_options;
};
static int post_checkout_hook(struct commit *old, struct commit *new,
@@ -149,6 +156,10 @@ static int checkout_merged(int pos, struct checkout *state)
read_mmblob(&ours, active_cache[pos+1]->sha1);
read_mmblob(&theirs, active_cache[pos+2]->sha1);
+ /*
+ * NEEDSWORK: re-create conflicts from merges with
+ * merge.renormalize set, too
+ */
status = ll_merge(&result_buf, path, &ancestor, "base",
&ours, "ours", &theirs, "theirs", 0);
free(ancestor.ptr);
@@ -273,12 +284,12 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,
return errs;
}
-static void show_local_changes(struct object *head)
+static void show_local_changes(struct object *head, struct diff_options *opts)
{
struct rev_info rev;
/* I think we want full paths, even if we're in a subdirectory. */
init_revisions(&rev, NULL);
- rev.abbrev = 0;
+ rev.diffopt.flags = opts->flags;
rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS;
if (diff_setup_done(&rev.diffopt) < 0)
die("diff_setup_done failed");
@@ -372,7 +383,7 @@ static int merge_working_tree(struct checkout_opts *opts,
topts.src_index = &the_index;
topts.dst_index = &the_index;
- topts.msgs.not_uptodate_file = "You have local changes to '%s'; cannot switch branches.";
+ setup_unpack_trees_porcelain(&topts, "checkout");
refresh_cache(REFRESH_QUIET);
@@ -432,6 +443,13 @@ static int merge_working_tree(struct checkout_opts *opts,
*/
add_files_to_cache(NULL, NULL, 0);
+ /*
+ * NEEDSWORK: carrying over local changes
+ * when branches have different end-of-line
+ * normalization (or clean+smudge rules) is
+ * a pain; plumb in an option to set
+ * o.renormalize?
+ */
init_merge_options(&o);
o.verbosity = 0;
work = write_tree_from_memory(&o);
@@ -455,7 +473,7 @@ static int merge_working_tree(struct checkout_opts *opts,
die("unable to write new index file");
if (!opts->force && !opts->quiet)
- show_local_changes(&new->commit->object);
+ show_local_changes(&new->commit->object, &opts->diff_options);
return 0;
}
@@ -492,8 +510,27 @@ static void update_refs_for_switch(struct checkout_opts *opts,
struct strbuf msg = STRBUF_INIT;
const char *old_desc;
if (opts->new_branch) {
- create_branch(old->name, opts->new_branch, new->name, 0,
- opts->new_branch_log, opts->track);
+ if (opts->new_orphan_branch) {
+ if (opts->new_branch_log && !log_all_ref_updates) {
+ int temp;
+ char log_file[PATH_MAX];
+ char *ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch);
+
+ temp = log_all_ref_updates;
+ log_all_ref_updates = 1;
+ if (log_ref_setup(ref_name, log_file, sizeof(log_file))) {
+ fprintf(stderr, "Can not do reflog for '%s'\n",
+ opts->new_orphan_branch);
+ log_all_ref_updates = temp;
+ return;
+ }
+ log_all_ref_updates = temp;
+ }
+ }
+ else
+ create_branch(old->name, opts->new_branch, new->name,
+ opts->new_branch_force ? 1 : 0,
+ opts->new_branch_log, opts->track);
new->name = opts->new_branch;
setup_branch_path(new);
}
@@ -510,11 +547,22 @@ static void update_refs_for_switch(struct checkout_opts *opts,
if (old->path && !strcmp(new->path, old->path))
fprintf(stderr, "Already on '%s'\n",
new->name);
- else
+ else if (opts->new_branch)
fprintf(stderr, "Switched to%s branch '%s'\n",
- opts->new_branch ? " a new" : "",
+ opts->branch_exists ? " and reset" : " a new",
+ new->name);
+ else
+ fprintf(stderr, "Switched to branch '%s'\n",
new->name);
}
+ if (old->path && old->name) {
+ char log_file[PATH_MAX], ref_file[PATH_MAX];
+
+ git_snpath(log_file, sizeof(log_file), "logs/%s", old->path);
+ git_snpath(ref_file, sizeof(ref_file), "%s", old->path);
+ if (!file_exists(ref_file) && file_exists(log_file))
+ remove_path(log_file);
+ }
} else if (strcmp(new->name, "HEAD")) {
update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL,
REF_NODEREF, DIE_ON_ERR);
@@ -573,7 +621,16 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
static int git_checkout_config(const char *var, const char *value, void *cb)
{
- return git_xmerge_config(var, value, cb);
+ if (!strcmp(var, "diff.ignoresubmodules")) {
+ struct checkout_opts *opts = cb;
+ handle_ignore_submodules_arg(&opts->diff_options, value);
+ return 0;
+ }
+
+ if (!prefixcmp(var, "submodule."))
+ return parse_submodule_config_option(var, value);
+
+ return git_xmerge_config(var, value, NULL);
}
static int interactive_checkout(const char *revision, const char **pathspec,
@@ -609,7 +666,8 @@ static int check_tracking_name(const char *refname, const unsigned char *sha1,
static const char *unique_tracking_name(const char *name)
{
- struct tracking_name_data cb_data = { name, NULL, 1 };
+ struct tracking_name_data cb_data = { NULL, NULL, 1 };
+ cb_data.name = name;
for_each_ref(check_tracking_name, &cb_data);
if (cb_data.unique)
return cb_data.remote;
@@ -629,10 +687,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
int dwim_new_local_branch = 1;
struct option options[] = {
OPT__QUIET(&opts.quiet),
- OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"),
+ OPT_STRING('b', NULL, &opts.new_branch, "branch",
+ "create and checkout a new branch"),
+ OPT_STRING('B', NULL, &opts.new_branch_force, "branch",
+ "create/reset and checkout a branch"),
OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"),
OPT_SET_INT('t', "track", &opts.track, "track",
BRANCH_TRACK_EXPLICIT),
+ OPT_STRING(0, "orphan", &opts.new_orphan_branch, "new branch", "new unparented branch"),
OPT_SET_INT('2', "ours", &opts.writeout_stage, "stage",
2),
OPT_SET_INT('3', "theirs", &opts.writeout_stage, "stage",
@@ -652,13 +714,22 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
memset(&opts, 0, sizeof(opts));
memset(&new, 0, sizeof(new));
- git_config(git_checkout_config, NULL);
+ gitmodules_config();
+ git_config(git_checkout_config, &opts);
opts.track = BRANCH_TRACK_UNSPECIFIED;
argc = parse_options(argc, argv, prefix, options, checkout_usage,
PARSE_OPT_KEEP_DASHDASH);
+ /* we can assume from now on new_branch = !new_branch_force */
+ if (opts.new_branch && opts.new_branch_force)
+ die("-B cannot be used with -b");
+
+ /* copy -B over to -b, so that we can just check the latter */
+ if (opts.new_branch_force)
+ opts.new_branch = opts.new_branch_force;
+
if (patch_mode && (opts.track > 0 || opts.new_branch
|| opts.new_branch_log || opts.merge || opts.force))
die ("--patch is incompatible with all other options");
@@ -678,6 +749,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
opts.new_branch = argv0 + 1;
}
+ if (opts.new_orphan_branch) {
+ if (opts.new_branch)
+ die("--orphan and -b|-B are mutually exclusive");
+ if (opts.track > 0)
+ die("--orphan cannot be used with -t");
+ opts.new_branch = opts.new_orphan_branch;
+ }
+
if (conflict_style) {
opts.merge = 1; /* implied */
git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
@@ -821,8 +900,12 @@ no_reference:
if (strbuf_check_branch_ref(&buf, opts.new_branch))
die("git checkout: we do not like '%s' as a branch name.",
opts.new_branch);
- if (!get_sha1(buf.buf, rev))
- die("git checkout: branch %s already exists", opts.new_branch);
+ if (!get_sha1(buf.buf, rev)) {
+ opts.branch_exists = 1;
+ if (!opts.new_branch_force)
+ die("git checkout: branch %s already exists",
+ opts.new_branch);
+ }
strbuf_release(&buf);
}
diff --git a/builtin/clean.c b/builtin/clean.c
index fac64e6cd3..c8798f549e 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -10,12 +10,13 @@
#include "cache.h"
#include "dir.h"
#include "parse-options.h"
+#include "string-list.h"
#include "quote.h"
static int force = -1; /* unset */
static const char *const builtin_clean_usage[] = {
- "git clean [-d] [-f] [-n] [-q] [-x | -X] [--] <paths>...",
+ "git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>...",
NULL
};
@@ -26,6 +27,13 @@ static int git_clean_config(const char *var, const char *value, void *cb)
return git_default_config(var, value, cb);
}
+static int exclude_cb(const struct option *opt, const char *arg, int unset)
+{
+ struct string_list *exclude_list = opt->value;
+ string_list_append(exclude_list, arg);
+ return 0;
+}
+
int cmd_clean(int argc, const char **argv, const char *prefix)
{
int i;
@@ -36,6 +44,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
struct dir_struct dir;
static const char **pathspec;
struct strbuf buf = STRBUF_INIT;
+ struct string_list exclude_list = STRING_LIST_INIT_NODUP;
const char *qname;
char *seen = NULL;
struct option options[] = {
@@ -44,6 +53,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
OPT_BOOLEAN('f', "force", &force, "force"),
OPT_BOOLEAN('d', NULL, &remove_directories,
"remove whole directories"),
+ { OPTION_CALLBACK, 'e', "exclude", &exclude_list, "pattern",
+ "exclude <pattern>", PARSE_OPT_NONEG, exclude_cb },
OPT_BOOLEAN('x', NULL, &ignored, "remove ignored files, too"),
OPT_BOOLEAN('X', NULL, &ignored_only,
"remove only ignored files"),
@@ -81,6 +92,9 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
if (!ignored)
setup_standard_excludes(&dir);
+ for (i = 0; i < exclude_list.nr; i++)
+ add_exclude(exclude_list.items[i].string, "", 0, dir.exclude_list);
+
pathspec = get_pathspec(prefix, argv);
fill_directory(&dir, pathspec);
@@ -167,5 +181,6 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
free(seen);
strbuf_release(&directory);
+ string_list_clear(&exclude_list, 0);
return (errors != 0);
}
diff --git a/builtin/clone.c b/builtin/clone.c
index 05f8fb4771..19ed64041d 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -302,6 +302,8 @@ static const struct ref *clone_local(const char *src_repo,
transport = transport_get(remote, src_repo);
ret = transport_get_remote_refs(transport);
transport_disconnect(transport);
+ if (0 <= option_verbosity)
+ printf("done.\n");
return ret;
}
@@ -359,7 +361,7 @@ static void write_remote_refs(const struct ref *local_refs)
int cmd_clone(int argc, const char **argv, const char *prefix)
{
- int is_bundle = 0;
+ int is_bundle = 0, is_local;
struct stat buf;
const char *repo_name, *repo, *work_tree, *git_dir;
char *path, *dir;
@@ -412,6 +414,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
repo = xstrdup(make_absolute_path(repo_name));
else
repo = repo_name;
+ is_local = path && !is_bundle;
+ if (is_local && option_depth)
+ warning("--depth is ignored in local clones; use file:// instead.");
if (argc == 2)
dir = xstrdup(argv[1]);
@@ -461,7 +466,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
die("could not create leading directories of '%s'", git_dir);
set_git_dir(make_absolute_path(git_dir));
- init_db(option_template, (option_verbosity < 0) ? INIT_DB_QUIET : 0);
+ if (0 <= option_verbosity)
+ printf("Cloning into %s%s...\n",
+ option_bare ? "bare repository " : "", dir);
+ init_db(option_template, INIT_DB_QUIET);
/*
* At this point, the config exists, so we do not need the
@@ -470,9 +478,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
*/
unsetenv(CONFIG_ENVIRONMENT);
- if (option_reference)
- setup_reference(git_dir);
-
git_config(git_default_config, NULL);
if (option_bare) {
@@ -498,22 +503,25 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
git_config_set(key.buf, "true");
strbuf_reset(&key);
}
-
- strbuf_addf(&key, "remote.%s.url", option_origin);
- git_config_set(key.buf, repo);
- strbuf_reset(&key);
}
+ strbuf_addf(&key, "remote.%s.url", option_origin);
+ git_config_set(key.buf, repo);
+ strbuf_reset(&key);
+
+ if (option_reference)
+ setup_reference(git_dir);
+
fetch_pattern = value.buf;
refspec = parse_fetch_refspec(1, &fetch_pattern);
strbuf_reset(&value);
- if (path && !is_bundle) {
+ if (is_local) {
refs = clone_local(path, git_dir);
mapped_refs = wanted_peer_refs(refs, refspec);
} else {
- struct remote *remote = remote_get(argv[0]);
+ struct remote *remote = remote_get(option_origin);
transport = transport_get(remote, remote->url[0]);
if (!transport->get_refs_list || !transport->fetch)
diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c
index 90dac349a3..87f0591c2f 100644
--- a/builtin/commit-tree.c
+++ b/builtin/commit-tree.c
@@ -9,19 +9,6 @@
#include "builtin.h"
#include "utf8.h"
-/*
- * FIXME! Share the code with "write-tree.c"
- */
-static void check_valid(unsigned char *sha1, enum object_type expect)
-{
- enum object_type type = sha1_object_info(sha1, NULL);
- if (type < 0)
- die("%s is not a valid object", sha1_to_hex(sha1));
- if (type != expect)
- die("%s is not a valid '%s' object", sha1_to_hex(sha1),
- typename(expect));
-}
-
static const char commit_tree_usage[] = "git commit-tree <sha1> [-p <sha1>]* < changelog";
static void new_parent(struct commit *parent, struct commit_list **parents_p)
@@ -38,61 +25,6 @@ static void new_parent(struct commit *parent, struct commit_list **parents_p)
commit_list_insert(parent, parents_p);
}
-static const char commit_utf8_warn[] =
-"Warning: commit message does not conform to UTF-8.\n"
-"You may want to amend it after fixing the message, or set the config\n"
-"variable i18n.commitencoding to the encoding your project uses.\n";
-
-int commit_tree(const char *msg, unsigned char *tree,
- struct commit_list *parents, unsigned char *ret,
- const char *author)
-{
- int result;
- int encoding_is_utf8;
- struct strbuf buffer;
-
- check_valid(tree, OBJ_TREE);
-
- /* Not having i18n.commitencoding is the same as having utf-8 */
- encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
-
- strbuf_init(&buffer, 8192); /* should avoid reallocs for the headers */
- strbuf_addf(&buffer, "tree %s\n", sha1_to_hex(tree));
-
- /*
- * NOTE! This ordering means that the same exact tree merged with a
- * different order of parents will be a _different_ changeset even
- * if everything else stays the same.
- */
- while (parents) {
- struct commit_list *next = parents->next;
- strbuf_addf(&buffer, "parent %s\n",
- sha1_to_hex(parents->item->object.sha1));
- free(parents);
- parents = next;
- }
-
- /* Person/date information */
- if (!author)
- author = git_author_info(IDENT_ERROR_ON_NO_NAME);
- strbuf_addf(&buffer, "author %s\n", author);
- strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME));
- if (!encoding_is_utf8)
- strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
- strbuf_addch(&buffer, '\n');
-
- /* And add the comment */
- strbuf_addstr(&buffer, msg);
-
- /* And check the encoding */
- if (encoding_is_utf8 && !is_utf8(buffer.buf))
- fprintf(stderr, commit_utf8_warn);
-
- result = write_sha1_file(buffer.buf, buffer.len, commit_type, ret);
- strbuf_release(&buffer);
- return result;
-}
-
int cmd_commit_tree(int argc, const char **argv, const char *prefix)
{
int i;
@@ -117,7 +49,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
if (get_sha1(b, sha1))
die("Not a valid object name %s", b);
- check_valid(sha1, OBJ_COMMIT);
+ assert_sha1_type(sha1, OBJ_COMMIT);
new_parent(lookup_commit(sha1), &parents);
}
diff --git a/builtin/commit.c b/builtin/commit.c
index c5ab683d5b..66fdd22024 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -25,6 +25,7 @@
#include "rerere.h"
#include "unpack-trees.h"
#include "quote.h"
+#include "submodule.h"
static const char * const builtin_commit_usage[] = {
"git commit [options] [--] <filepattern>...",
@@ -48,6 +49,11 @@ static const char implicit_ident_advice[] =
"\n"
" git commit --amend --author='Your Name <you@example.com>'\n";
+static const char empty_amend_advice[] =
+"You asked to amend the most recent commit, but doing so would make\n"
+"it empty. You can repeat your command with --allow-empty, or you can\n"
+"remove the commit entirely with \"git reset HEAD^\".\n";
+
static unsigned char head_sha1[20];
static char *use_message_buffer;
@@ -57,7 +63,7 @@ static struct lock_file false_lock; /* used only for partial commits */
static enum {
COMMIT_AS_IS = 1,
COMMIT_NORMAL,
- COMMIT_PARTIAL,
+ COMMIT_PARTIAL
} commit_style;
static const char *logfile, *force_author;
@@ -66,8 +72,8 @@ static char *edit_message, *use_message;
static char *author_name, *author_email, *author_date;
static int all, edit_flag, also, interactive, only, amend, signoff;
static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
-static int no_post_rewrite;
-static char *untracked_files_arg, *force_date;
+static int no_post_rewrite, allow_empty_message;
+static char *untracked_files_arg, *force_date, *ignore_submodule_arg;
/*
* The default commit message cleanup mode will remove the lines
* beginning with # (shell comments) and leading and trailing
@@ -78,11 +84,12 @@ static char *untracked_files_arg, *force_date;
static enum {
CLEANUP_SPACE,
CLEANUP_NONE,
- CLEANUP_ALL,
+ CLEANUP_ALL
} cleanup_mode;
static char *cleanup_arg;
static int use_editor = 1, initial_commit, in_merge, include_status = 1;
+static int show_ignored_in_status;
static const char *only_include_assumed;
static struct strbuf message;
@@ -90,8 +97,9 @@ static int null_termination;
static enum {
STATUS_FORMAT_LONG,
STATUS_FORMAT_SHORT,
- STATUS_FORMAT_PORCELAIN,
+ STATUS_FORMAT_PORCELAIN
} status_format = STATUS_FORMAT_LONG;
+static int status_show_branch;
static int opt_parse_m(const struct option *opt, const char *arg, int unset)
{
@@ -133,16 +141,23 @@ static struct option builtin_commit_options[] = {
OPT_BOOLEAN(0, "dry-run", &dry_run, "show what would be committed"),
OPT_SET_INT(0, "short", &status_format, "show status concisely",
STATUS_FORMAT_SHORT),
+ OPT_BOOLEAN(0, "branch", &status_show_branch, "show branch information"),
OPT_SET_INT(0, "porcelain", &status_format,
"show porcelain output format", STATUS_FORMAT_PORCELAIN),
OPT_BOOLEAN('z', "null", &null_termination,
"terminate entries with NUL"),
OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
OPT_BOOLEAN(0, "no-post-rewrite", &no_post_rewrite, "bypass post-rewrite hook"),
- { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
- OPT_BOOLEAN(0, "allow-empty", &allow_empty, "ok to record an empty change"),
+ { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
/* end commit contents options */
+ { OPTION_BOOLEAN, 0, "allow-empty", &allow_empty, NULL,
+ "ok to record an empty change",
+ PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
+ { OPTION_BOOLEAN, 0, "allow-empty-message", &allow_empty_message, NULL,
+ "ok to record a change with an empty message",
+ PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
+
OPT_END()
};
@@ -205,7 +220,7 @@ static int list_paths(struct string_list *list, const char *with_tree,
continue;
if (!match_pathspec(pattern, ce->name, ce_namelen(ce), 0, m))
continue;
- item = string_list_insert(ce->name, list);
+ item = string_list_insert(list, ce->name);
if (ce_skip_worktree(ce))
item->util = item; /* better a valid pointer than a fake one */
}
@@ -329,9 +344,13 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, int
if (!pathspec || !*pathspec) {
fd = hold_locked_index(&index_lock, 1);
refresh_cache_or_die(refresh_flags);
- if (write_cache(fd, active_cache, active_nr) ||
- commit_locked_index(&index_lock))
- die("unable to write new_index file");
+ if (active_cache_changed) {
+ if (write_cache(fd, active_cache, active_nr) ||
+ commit_locked_index(&index_lock))
+ die("unable to write new_index file");
+ } else {
+ rollback_lock_file(&index_lock);
+ }
commit_style = COMMIT_AS_IS;
return get_index_file();
}
@@ -417,7 +436,7 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
switch (status_format) {
case STATUS_FORMAT_SHORT:
- wt_shortstatus_print(s, null_termination);
+ wt_shortstatus_print(s, null_termination, status_show_branch);
break;
case STATUS_FORMAT_PORCELAIN:
wt_porcelain_print(s, null_termination);
@@ -455,15 +474,21 @@ static void determine_author_info(void)
if (!a)
die("invalid commit: %s", use_message);
- lb = strstr(a + 8, " <");
- rb = strstr(a + 8, "> ");
- eol = strchr(a + 8, '\n');
- if (!lb || !rb || !eol)
+ lb = strchrnul(a + strlen("\nauthor "), '<');
+ rb = strchrnul(lb, '>');
+ eol = strchrnul(rb, '\n');
+ if (!*lb || !*rb || !*eol)
die("invalid commit: %s", use_message);
- name = xstrndup(a + 8, lb - (a + 8));
- email = xstrndup(lb + 2, rb - (lb + 2));
- date = xstrndup(rb + 2, eol - (rb + 2));
+ if (lb == a + strlen("\nauthor "))
+ /* \nauthor <foo@example.com> */
+ name = xcalloc(1, 1);
+ else
+ name = xmemdupz(a + strlen("\nauthor "),
+ (lb - strlen(" ") -
+ (a + strlen("\nauthor "))));
+ email = xmemdupz(lb + strlen("<"), rb - (lb + strlen("<")));
+ date = xmemdupz(rb + strlen("> "), eol - (rb + strlen("> ")));
}
if (force_author) {
@@ -693,6 +718,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
if (!commitable && !in_merge && !allow_empty &&
!(amend && is_a_merge(head_sha1))) {
run_status(stdout, index_file, prefix, 0, s);
+ if (amend)
+ fputs(empty_amend_advice, stderr);
return 0;
}
@@ -717,7 +744,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
if (use_editor) {
char index[PATH_MAX];
- const char *env[2] = { index, NULL };
+ const char *env[2] = { NULL };
+ env[0] = index;
snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
if (launch_editor(git_path(commit_editmsg), NULL, env)) {
fprintf(stderr,
@@ -1017,11 +1045,14 @@ static int git_status_config(const char *k, const char *v, void *cb)
int cmd_status(int argc, const char **argv, const char *prefix)
{
struct wt_status s;
+ int fd;
unsigned char sha1[20];
static struct option builtin_status_options[] = {
OPT__VERBOSE(&verbose),
OPT_SET_INT('s', "short", &status_format,
"show status concisely", STATUS_FORMAT_SHORT),
+ OPT_BOOLEAN('b', "branch", &status_show_branch,
+ "show branch information"),
OPT_SET_INT(0, "porcelain", &status_format,
"show porcelain output format",
STATUS_FORMAT_PORCELAIN),
@@ -1031,6 +1062,11 @@ int cmd_status(int argc, const char **argv, const char *prefix)
"mode",
"show untracked files, optional modes: all, normal, no. (Default: all)",
PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
+ OPT_BOOLEAN(0, "ignored", &show_ignored_in_status,
+ "show ignored files"),
+ { OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, "when",
+ "ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)",
+ PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
OPT_END(),
};
@@ -1038,20 +1074,33 @@ int cmd_status(int argc, const char **argv, const char *prefix)
status_format = STATUS_FORMAT_PORCELAIN;
wt_status_prepare(&s);
+ gitmodules_config();
git_config(git_status_config, &s);
in_merge = file_exists(git_path("MERGE_HEAD"));
argc = parse_options(argc, argv, prefix,
builtin_status_options,
builtin_status_usage, 0);
handle_untracked_files_arg(&s);
-
+ if (show_ignored_in_status)
+ s.show_ignored_files = 1;
if (*argv)
s.pathspec = get_pathspec(prefix, argv);
read_cache_preload(s.pathspec);
refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, s.pathspec, NULL, NULL);
+
+ fd = hold_locked_index(&index_lock, 0);
+ if (0 <= fd) {
+ if (active_cache_changed &&
+ !write_cache(fd, active_cache, active_nr))
+ commit_locked_index(&index_lock);
+ else
+ rollback_lock_file(&index_lock);
+ }
+
s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;
s.in_merge = in_merge;
+ s.ignore_submodule_arg = ignore_submodule_arg;
wt_status_collect(&s);
if (s.relative_paths)
@@ -1063,13 +1112,14 @@ int cmd_status(int argc, const char **argv, const char *prefix)
switch (status_format) {
case STATUS_FORMAT_SHORT:
- wt_shortstatus_print(&s, null_termination);
+ wt_shortstatus_print(&s, null_termination, status_show_branch);
break;
case STATUS_FORMAT_PORCELAIN:
wt_porcelain_print(&s, null_termination);
break;
case STATUS_FORMAT_LONG:
s.verbose = verbose;
+ s.ignore_submodule_arg = ignore_submodule_arg;
wt_status_print(&s);
break;
}
@@ -1115,7 +1165,6 @@ static void print_summary(const char *prefix, const unsigned char *sha1)
init_revisions(&rev, prefix);
setup_revisions(0, NULL, &rev, NULL);
- rev.abbrev = 0;
rev.diff = 1;
rev.diffopt.output_format =
DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;
@@ -1138,13 +1187,11 @@ static void print_summary(const char *prefix, const unsigned char *sha1)
initial_commit ? " (root-commit)" : "");
if (!log_tree_commit(&rev, commit)) {
- struct pretty_print_context ctx = {0};
- struct strbuf buf = STRBUF_INIT;
- ctx.date_mode = DATE_NORMAL;
- format_commit_message(commit, format.buf + 7, &buf, &ctx);
- printf("%s\n", buf.buf);
- strbuf_release(&buf);
+ rev.always_show_header = 1;
+ rev.use_terminator = 1;
+ log_tree_commit(&rev, commit);
}
+
strbuf_release(&format);
}
@@ -1232,13 +1279,16 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
}
/* Determine parents */
+ reflog_msg = getenv("GIT_REFLOG_ACTION");
if (initial_commit) {
- reflog_msg = "commit (initial)";
+ if (!reflog_msg)
+ reflog_msg = "commit (initial)";
} else if (amend) {
struct commit_list *c;
struct commit *commit;
- reflog_msg = "commit (amend)";
+ if (!reflog_msg)
+ reflog_msg = "commit (amend)";
commit = lookup_commit(head_sha1);
if (!commit || parse_commit(commit))
die("could not parse HEAD commit");
@@ -1249,7 +1299,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
struct strbuf m = STRBUF_INIT;
FILE *fp;
- reflog_msg = "commit (merge)";
+ if (!reflog_msg)
+ reflog_msg = "commit (merge)";
pptr = &commit_list_insert(lookup_commit(head_sha1), pptr)->next;
fp = fopen(git_path("MERGE_HEAD"), "r");
if (fp == NULL)
@@ -1272,7 +1323,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
if (allow_fast_forward)
parents = reduce_heads(parents);
} else {
- reflog_msg = "commit";
+ if (!reflog_msg)
+ reflog_msg = "commit";
pptr = &commit_list_insert(lookup_commit(head_sha1), pptr)->next;
}
@@ -1293,7 +1345,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
if (cleanup_mode != CLEANUP_NONE)
stripspace(&sb, cleanup_mode == CLEANUP_ALL);
- if (message_is_empty(&sb)) {
+ if (message_is_empty(&sb) && !allow_empty_message) {
rollback_index_files();
fprintf(stderr, "Aborting commit due to empty commit message.\n");
exit(1);
diff --git a/builtin/config.c b/builtin/config.c
index 4bc46b15fd..ca4a0db4a7 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -20,7 +20,7 @@ static char delim = '=';
static char key_delim = ' ';
static char term = '\n';
-static int use_global_config, use_system_config;
+static int use_global_config, use_system_config, use_local_config;
static const char *given_config_file;
static int actions, types;
static const char *get_color_slot, *get_colorbool_slot;
@@ -51,6 +51,7 @@ static struct option builtin_config_options[] = {
OPT_GROUP("Config file location"),
OPT_BOOLEAN(0, "global", &use_global_config, "use global config file"),
OPT_BOOLEAN(0, "system", &use_system_config, "use system config file"),
+ OPT_BOOLEAN(0, "local", &use_local_config, "use repository config file"),
OPT_STRING('f', "file", &given_config_file, "FILE", "use given config file"),
OPT_GROUP("Action"),
OPT_BIT(0, "get", &actions, "get value: name [value-regex]", ACTION_GET),
@@ -197,7 +198,11 @@ static int get_value(const char *key_, const char *regex_)
git_config_from_file(show_config, system_wide, NULL);
if (do_all && global)
git_config_from_file(show_config, global, NULL);
- git_config_from_file(show_config, local, NULL);
+ if (do_all)
+ git_config_from_file(show_config, local, NULL);
+ git_config_from_parameters(show_config, NULL);
+ if (!do_all && !seen)
+ git_config_from_file(show_config, local, NULL);
if (!do_all && !seen && global)
git_config_from_file(show_config, global, NULL);
if (!do_all && !seen && system_wide)
@@ -326,11 +331,10 @@ static int get_colorbool(int print)
return get_colorbool_found ? 0 : 1;
}
-int cmd_config(int argc, const char **argv, const char *unused_prefix)
+int cmd_config(int argc, const char **argv, const char *prefix)
{
- int nongit;
+ int nongit = !startup_info->have_repository;
char *value;
- const char *prefix = setup_git_directory_gently(&nongit);
config_exclusive_filename = getenv(CONFIG_ENVIRONMENT);
@@ -338,7 +342,7 @@ int cmd_config(int argc, const char **argv, const char *unused_prefix)
builtin_config_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
- if (use_global_config + use_system_config + !!given_config_file > 1) {
+ if (use_global_config + use_system_config + use_local_config + !!given_config_file > 1) {
error("only one config file at a time.");
usage_with_options(builtin_config_usage, builtin_config_options);
}
@@ -354,6 +358,8 @@ int cmd_config(int argc, const char **argv, const char *unused_prefix)
}
else if (use_system_config)
config_exclusive_filename = git_etc_gitconfig();
+ else if (use_local_config)
+ config_exclusive_filename = git_pathdup("config");
else if (given_config_file) {
if (!is_absolute_path(given_config_file) && prefix)
config_exclusive_filename = prefix_filename(prefix,
diff --git a/builtin/describe.c b/builtin/describe.c
index 71be2a9364..43caff2ffe 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -35,7 +35,8 @@ static const char *diff_index_args[] = {
struct commit_name {
struct tag *tag;
- int prio; /* annotated tag = 2, tag = 1, head = 0 */
+ unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */
+ unsigned name_checked:1;
unsigned char sha1[20];
char path[FLEX_ARRAY]; /* more */
};
@@ -43,18 +44,53 @@ static const char *prio_names[] = {
"head", "lightweight", "annotated",
};
+static int replace_name(struct commit_name *e,
+ int prio,
+ const unsigned char *sha1,
+ struct tag **tag)
+{
+ if (!e || e->prio < prio)
+ return 1;
+
+ if (e->prio == 2 && prio == 2) {
+ /* Multiple annotated tags point to the same commit.
+ * Select one to keep based upon their tagger date.
+ */
+ struct tag *t;
+
+ if (!e->tag) {
+ t = lookup_tag(e->sha1);
+ if (!t || parse_tag(t))
+ return 1;
+ e->tag = t;
+ }
+
+ t = lookup_tag(sha1);
+ if (!t || parse_tag(t))
+ return 0;
+ *tag = t;
+
+ if (e->tag->date < t->date)
+ return 1;
+ }
+
+ return 0;
+}
+
static void add_to_known_names(const char *path,
struct commit *commit,
int prio,
const unsigned char *sha1)
{
struct commit_name *e = commit->util;
- if (!e || e->prio < prio) {
+ struct tag *tag = NULL;
+ if (replace_name(e, prio, sha1, &tag)) {
size_t len = strlen(path)+1;
free(e);
e = xmalloc(sizeof(struct commit_name) + len);
- e->tag = NULL;
+ e->tag = tag;
e->prio = prio;
+ e->name_checked = 0;
hashcpy(e->sha1, sha1);
memcpy(e->path, path, len);
commit->util = e;
@@ -165,10 +201,15 @@ static void display_name(struct commit_name *n)
{
if (n->prio == 2 && !n->tag) {
n->tag = lookup_tag(n->sha1);
- if (!n->tag || parse_tag(n->tag) || !n->tag->tag)
+ if (!n->tag || parse_tag(n->tag))
die("annotated tag %s not available", n->path);
+ }
+ if (n->tag && !n->name_checked) {
+ if (!n->tag->tag)
+ die("annotated tag %s has no embedded name", n->path);
if (strcmp(n->tag->tag, all ? n->path + 5 : n->path))
warning("tag '%s' is really '%s' here", n->tag->tag, n->path);
+ n->name_checked = 1;
}
if (n->tag)
diff --git a/builtin/diff-files.c b/builtin/diff-files.c
index 5b64011de8..951c7c8994 100644
--- a/builtin/diff-files.c
+++ b/builtin/diff-files.c
@@ -8,6 +8,7 @@
#include "commit.h"
#include "revision.h"
#include "builtin.h"
+#include "submodule.h"
static const char diff_files_usage[] =
"git diff-files [-q] [-0/-1/2/3 |-c|--cc] [<common diff options>] [<path>...]"
@@ -20,6 +21,7 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
unsigned options = 0;
init_revisions(&rev, prefix);
+ gitmodules_config();
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
rev.abbrev = 0;
diff --git a/builtin/diff-index.c b/builtin/diff-index.c
index 04837494fe..2eb32bd9da 100644
--- a/builtin/diff-index.c
+++ b/builtin/diff-index.c
@@ -3,6 +3,7 @@
#include "commit.h"
#include "revision.h"
#include "builtin.h"
+#include "submodule.h"
static const char diff_cache_usage[] =
"git diff-index [-m] [--cached] "
@@ -17,6 +18,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
int result;
init_revisions(&rev, prefix);
+ gitmodules_config();
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
rev.abbrev = 0;
diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c
index 3c78bda566..0d2a3e9fa2 100644
--- a/builtin/diff-tree.c
+++ b/builtin/diff-tree.c
@@ -3,6 +3,7 @@
#include "commit.h"
#include "log-tree.h"
#include "builtin.h"
+#include "submodule.h"
static struct rev_info log_tree_opt;
@@ -112,6 +113,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
int read_stdin = 0;
init_revisions(opt, prefix);
+ gitmodules_config();
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
opt->abbrev = 0;
opt->diff = 1;
diff --git a/builtin/diff.c b/builtin/diff.c
index ffcdd055ca..a43d326363 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -13,6 +13,7 @@
#include "revision.h"
#include "log-tree.h"
#include "builtin.h"
+#include "submodule.h"
struct blobinfo {
unsigned char sha1[20];
@@ -279,6 +280,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
*/
prefix = setup_git_directory_gently(&nongit);
+ gitmodules_config();
git_config(git_diff_ui_config, NULL);
if (diff_use_color_default == -1)
@@ -407,17 +409,19 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
result = builtin_diff_index(&rev, argc, argv);
else if (ents == 2)
result = builtin_diff_tree(&rev, argc, argv, ent);
- else if ((ents == 3) && (ent[0].item->flags & UNINTERESTING)) {
- /* diff A...B where there is one sane merge base between
- * A and B. We have ent[0] == merge-base, ent[1] == A,
- * and ent[2] == B. Show diff between the base and B.
+ else if (ent[0].item->flags & UNINTERESTING) {
+ /*
+ * diff A...B where there is at least one merge base
+ * between A and B. We have ent[0] == merge-base,
+ * ent[ents-2] == A, and ent[ents-1] == B. Show diff
+ * between the base and B. Note that we pick one
+ * merge base at random if there are more than one.
*/
- ent[1] = ent[2];
+ ent[1] = ent[ents-1];
result = builtin_diff_tree(&rev, argc, argv, ent);
- }
- else
+ } else
result = builtin_diff_combined(&rev, argc, argv,
- ent, ents);
+ ent, ents);
result = diff_result_code(&rev.diffopt, result);
if (1 < rev.diffopt.skip_stat_unmatch)
refresh_index_quietly();
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index c6dd71a7bc..c8fd46b872 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -27,6 +27,7 @@ static enum { ABORT, VERBATIM, WARN, STRIP } signed_tag_mode = ABORT;
static enum { ERROR, DROP, REWRITE } tag_of_filtered_mode = ABORT;
static int fake_missing_tagger;
static int no_data;
+static int full_tree;
static int parse_opt_signed_tag_mode(const struct option *opt,
const char *arg, int unset)
@@ -147,10 +148,47 @@ static void handle_object(const unsigned char *sha1)
free(buf);
}
+static int depth_first(const void *a_, const void *b_)
+{
+ const struct diff_filepair *a = *((const struct diff_filepair **)a_);
+ const struct diff_filepair *b = *((const struct diff_filepair **)b_);
+ const char *name_a, *name_b;
+ int len_a, len_b, len;
+ int cmp;
+
+ name_a = a->one ? a->one->path : a->two->path;
+ name_b = b->one ? b->one->path : b->two->path;
+
+ len_a = strlen(name_a);
+ len_b = strlen(name_b);
+ len = (len_a < len_b) ? len_a : len_b;
+
+ /* strcmp will sort 'd' before 'd/e', we want 'd/e' before 'd' */
+ cmp = memcmp(name_a, name_b, len);
+ if (cmp)
+ return cmp;
+ cmp = len_b - len_a;
+ if (cmp)
+ return cmp;
+ /*
+ * Move 'R'ename entries last so that all references of the file
+ * appear in the output before it is renamed (e.g., when a file
+ * was copied and renamed in the same commit).
+ */
+ return (a->status == 'R') - (b->status == 'R');
+}
+
static void show_filemodify(struct diff_queue_struct *q,
struct diff_options *options, void *data)
{
int i;
+
+ /*
+ * Handle files below a directory first, in case they are all deleted
+ * and the directory changes to a file or symlink.
+ */
+ qsort(q->queue, q->nr, sizeof(q->queue[0]), depth_first);
+
for (i = 0; i < q->nr; i++) {
struct diff_filespec *ospec = q->queue[i]->one;
struct diff_filespec *spec = q->queue[i]->two;
@@ -241,7 +279,8 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
message += 2;
if (commit->parents &&
- get_object_mark(&commit->parents->item->object) != 0) {
+ get_object_mark(&commit->parents->item->object) != 0 &&
+ !full_tree) {
parse_commit(commit->parents->item);
diff_tree_sha1(commit->parents->item->tree->object.sha1,
commit->tree->object.sha1, "", &rev->diffopt);
@@ -281,6 +320,8 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
i++;
}
+ if (full_tree)
+ printf("deleteall\n");
log_tree_diff_flush(rev);
rev->diffopt.output_format = saved_output_format;
@@ -438,7 +479,7 @@ static void get_tags_and_duplicates(struct object_array *pending,
/* handle nested tags */
while (tag && tag->object.type == OBJ_TAG) {
parse_object(tag->object.sha1);
- string_list_append(full_name, extra_refs)->util = tag;
+ string_list_append(extra_refs, full_name)->util = tag;
tag = (struct tag *)tag->tagged;
}
if (!tag)
@@ -464,7 +505,7 @@ static void get_tags_and_duplicates(struct object_array *pending,
}
if (commit->util)
/* more than one name for the same object */
- string_list_append(full_name, extra_refs)->util = commit;
+ string_list_append(extra_refs, full_name)->util = commit;
else
commit->util = full_name;
}
@@ -565,8 +606,8 @@ static void import_marks(char *input_file)
int cmd_fast_export(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
- struct object_array commits = { 0, 0, NULL };
- struct string_list extra_refs = { NULL, 0, 0, 0 };
+ struct object_array commits = OBJECT_ARRAY_INIT;
+ struct string_list extra_refs = STRING_LIST_INIT_NODUP;
struct commit *commit;
char *export_filename = NULL, *import_filename = NULL;
struct option options[] = {
@@ -584,6 +625,8 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
"Import marks from this file"),
OPT_BOOLEAN(0, "fake-missing-tagger", &fake_missing_tagger,
"Fake a tagger when tags lack one"),
+ OPT_BOOLEAN(0, "full-tree", &full_tree,
+ "Output full tree for each commit"),
{ OPTION_NEGBIT, 0, "data", &no_data, NULL,
"Skip output of blob data",
PARSE_OPT_NOARG | PARSE_OPT_NEGHELP, NULL, 1 },
@@ -608,6 +651,9 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
if (import_filename)
import_marks(import_filename);
+ if (import_filename && revs.prune_data)
+ full_tree = 1;
+
get_tags_and_duplicates(&revs.pending, &extra_refs);
if (prepare_revision_walk(&revs))
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 3bfc081c6d..fccc9cbea3 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -530,7 +530,7 @@ static int add_existing(const char *refname, const unsigned char *sha1,
int flag, void *cbdata)
{
struct string_list *list = (struct string_list *)cbdata;
- struct string_list_item *item = string_list_insert(refname, list);
+ struct string_list_item *item = string_list_insert(list, refname);
item->util = (void *)sha1;
return 0;
}
@@ -546,37 +546,12 @@ static int will_fetch(struct ref **head, const unsigned char *sha1)
return 0;
}
-struct tag_data {
- struct ref **head;
- struct ref ***tail;
-};
-
-static int add_to_tail(struct string_list_item *item, void *cb_data)
-{
- struct tag_data *data = (struct tag_data *)cb_data;
- struct ref *rm = NULL;
-
- /* We have already decided to ignore this item */
- if (!item->util)
- return 0;
-
- rm = alloc_ref(item->string);
- rm->peer_ref = alloc_ref(item->string);
- hashcpy(rm->old_sha1, item->util);
-
- **data->tail = rm;
- *data->tail = &rm->next;
-
- return 0;
-}
-
static void find_non_local_tags(struct transport *transport,
struct ref **head,
struct ref ***tail)
{
- struct string_list existing_refs = { NULL, 0, 0, 0 };
- struct string_list remote_refs = { NULL, 0, 0, 0 };
- struct tag_data data = {head, tail};
+ struct string_list existing_refs = STRING_LIST_INIT_NODUP;
+ struct string_list remote_refs = STRING_LIST_INIT_NODUP;
const struct ref *ref;
struct string_list_item *item = NULL;
@@ -618,7 +593,7 @@ static void find_non_local_tags(struct transport *transport,
string_list_has_string(&existing_refs, ref->name))
continue;
- item = string_list_insert(ref->name, &remote_refs);
+ item = string_list_insert(&remote_refs, ref->name);
item->util = (void *)ref->old_sha1;
}
string_list_clear(&existing_refs, 0);
@@ -632,10 +607,20 @@ static void find_non_local_tags(struct transport *transport,
item->util = NULL;
/*
- * For all the tags in the remote_refs string list, call
- * add_to_tail to add them to the list of refs to be fetched
+ * For all the tags in the remote_refs string list,
+ * add them to the list of refs to be fetched
*/
- for_each_string_list(add_to_tail, &remote_refs, &data);
+ for_each_string_list_item(item, &remote_refs) {
+ /* Unless we have already decided to ignore this item... */
+ if (item->util)
+ {
+ struct ref *rm = alloc_ref(item->string);
+ rm->peer_ref = alloc_ref(item->string);
+ hashcpy(rm->old_sha1, item->util);
+ **tail = rm;
+ *tail = &rm->next;
+ }
+ }
string_list_clear(&remote_refs, 0);
}
@@ -668,7 +653,7 @@ static int truncate_fetch_head(void)
static int do_fetch(struct transport *transport,
struct refspec *refs, int ref_count)
{
- struct string_list existing_refs = { NULL, 0, 0, 0 };
+ struct string_list existing_refs = STRING_LIST_INIT_NODUP;
struct string_list_item *peer_item = NULL;
struct ref *ref_map;
struct ref *rm;
@@ -676,10 +661,12 @@ static int do_fetch(struct transport *transport,
for_each_ref(add_existing, &existing_refs);
- if (transport->remote->fetch_tags == 2 && tags != TAGS_UNSET)
- tags = TAGS_SET;
- if (transport->remote->fetch_tags == -1)
- tags = TAGS_UNSET;
+ if (tags == TAGS_DEFAULT) {
+ if (transport->remote->fetch_tags == 2)
+ tags = TAGS_SET;
+ if (transport->remote->fetch_tags == -1)
+ tags = TAGS_UNSET;
+ }
if (!transport->get_refs_list || !transport->fetch)
die("Don't know how to fetch from %s", transport->url);
@@ -697,8 +684,8 @@ static int do_fetch(struct transport *transport,
for (rm = ref_map; rm; rm = rm->next) {
if (rm->peer_ref) {
- peer_item = string_list_lookup(rm->peer_ref->name,
- &existing_refs);
+ peer_item = string_list_lookup(&existing_refs,
+ rm->peer_ref->name);
if (peer_item)
hashcpy(rm->peer_ref->old_sha1,
peer_item->util);
@@ -747,7 +734,7 @@ static int get_one_remote_for_fetch(struct remote *remote, void *priv)
{
struct string_list *list = priv;
if (!remote->skip_default_update)
- string_list_append(remote->name, list);
+ string_list_append(list, remote->name);
return 0;
}
@@ -766,8 +753,8 @@ static int get_remote_group(const char *key, const char *value, void *priv)
int space = strcspn(value, " \t\n");
while (*value) {
if (space > 1) {
- string_list_append(xstrndup(value, space),
- g->list);
+ string_list_append(g->list,
+ xstrndup(value, space));
}
value += space + (value[space] != '\0');
space = strcspn(value, " \t\n");
@@ -780,7 +767,8 @@ static int get_remote_group(const char *key, const char *value, void *priv)
static int add_remote_or_group(const char *name, struct string_list *list)
{
int prev_nr = list->nr;
- struct remote_group_data g = { name, list };
+ struct remote_group_data g;
+ g.name = name; g.list = list;
git_config(get_remote_group, &g);
if (list->nr == prev_nr) {
@@ -788,7 +776,7 @@ static int add_remote_or_group(const char *name, struct string_list *list)
if (!remote_is_configured(name))
return 0;
remote = remote_get(name);
- string_list_append(remote->name, list);
+ string_list_append(list, remote->name);
}
return 1;
}
@@ -845,7 +833,8 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
int exit_code;
if (!remote)
- die("Where do you want to fetch from today?");
+ die("No remote repository specified. Please, specify either a URL or a\n"
+ "remote name from which new revisions should be fetched.");
transport = transport_get(remote, NULL);
transport_set_verbosity(transport, verbosity, progress);
@@ -890,7 +879,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
int cmd_fetch(int argc, const char **argv, const char *prefix)
{
int i;
- struct string_list list = { NULL, 0, 0, 0 };
+ struct string_list list = STRING_LIST_INIT_NODUP;
struct remote *remote;
int result = 0;
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index 379a03131f..e7e12eea25 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -7,7 +7,7 @@
#include "string-list.h"
static const char * const fmt_merge_msg_usage[] = {
- "git fmt-merge-msg [--log|--no-log] [--file <file>]",
+ "git fmt-merge-msg [-m <message>] [--log|--no-log] [--file <file>]",
NULL
};
@@ -38,8 +38,8 @@ void init_src_data(struct src_data *data)
data->generic.strdup_strings = 1;
}
-static struct string_list srcs = { NULL, 0, 0, 1 };
-static struct string_list origins = { NULL, 0, 0, 1 };
+static struct string_list srcs = STRING_LIST_INIT_DUP;
+static struct string_list origins = STRING_LIST_INIT_DUP;
static int handle_line(char *line)
{
@@ -82,7 +82,7 @@ static int handle_line(char *line)
item = unsorted_string_list_lookup(&srcs, src);
if (!item) {
- item = string_list_append(src, &srcs);
+ item = string_list_append(&srcs, src);
item->util = xcalloc(1, sizeof(struct src_data));
init_src_data(item->util);
}
@@ -93,19 +93,19 @@ static int handle_line(char *line)
src_data->head_status |= 1;
} else if (!prefixcmp(line, "branch ")) {
origin = line + 7;
- string_list_append(origin, &src_data->branch);
+ string_list_append(&src_data->branch, origin);
src_data->head_status |= 2;
} else if (!prefixcmp(line, "tag ")) {
origin = line;
- string_list_append(origin + 4, &src_data->tag);
+ string_list_append(&src_data->tag, origin + 4);
src_data->head_status |= 2;
} else if (!prefixcmp(line, "remote branch ")) {
origin = line + 14;
- string_list_append(origin, &src_data->r_branch);
+ string_list_append(&src_data->r_branch, origin);
src_data->head_status |= 2;
} else {
origin = src;
- string_list_append(line, &src_data->generic);
+ string_list_append(&src_data->generic, line);
src_data->head_status |= 2;
}
@@ -118,7 +118,7 @@ static int handle_line(char *line)
sprintf(new_origin, "%s of %s", origin, src);
origin = new_origin;
}
- string_list_append(origin, &origins)->util = sha1;
+ string_list_append(&origins, origin)->util = sha1;
return 0;
}
@@ -146,7 +146,7 @@ static void shortlog(const char *name, unsigned char *sha1,
int i, count = 0;
struct commit *commit;
struct object *branch;
- struct string_list subjects = { NULL, 0, 0, 1 };
+ struct string_list subjects = STRING_LIST_INIT_DUP;
int flags = UNINTERESTING | TREESAME | SEEN | SHOWN | ADDED;
struct strbuf sb = STRBUF_INIT;
@@ -176,10 +176,10 @@ static void shortlog(const char *name, unsigned char *sha1,
strbuf_ltrim(&sb);
if (!sb.len)
- string_list_append(sha1_to_hex(commit->object.sha1),
- &subjects);
+ string_list_append(&subjects,
+ sha1_to_hex(commit->object.sha1));
else
- string_list_append(strbuf_detach(&sb, NULL), &subjects);
+ string_list_append(&subjects, strbuf_detach(&sb, NULL));
}
if (count > limit)
@@ -202,35 +202,10 @@ static void shortlog(const char *name, unsigned char *sha1,
string_list_clear(&subjects, 0);
}
-int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
- int limit = 20, i = 0, pos = 0;
+static void do_fmt_merge_msg_title(struct strbuf *out,
+ const char *current_branch) {
+ int i = 0;
char *sep = "";
- unsigned char head_sha1[20];
- const char *current_branch;
-
- /* get current branch */
- current_branch = resolve_ref("HEAD", head_sha1, 1, NULL);
- if (!current_branch)
- die("No current branch");
- if (!prefixcmp(current_branch, "refs/heads/"))
- current_branch += 11;
-
- /* get a line */
- while (pos < in->len) {
- int len;
- char *newline, *p = in->buf + pos;
-
- newline = strchr(p, '\n');
- len = newline ? newline - p : strlen(p);
- pos += len + !!newline;
- i++;
- p[len] = 0;
- if (handle_line(p))
- die ("Error in line %d: %.*s", i, len, p);
- }
-
- if (!srcs.nr)
- return 0;
strbuf_addstr(out, "Merge ");
for (i = 0; i < srcs.nr; i++) {
@@ -278,6 +253,40 @@ int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
strbuf_addch(out, '\n');
else
strbuf_addf(out, " into %s\n", current_branch);
+}
+
+static int do_fmt_merge_msg(int merge_title, int merge_summary,
+ struct strbuf *in, struct strbuf *out) {
+ int limit = 20, i = 0, pos = 0;
+ unsigned char head_sha1[20];
+ const char *current_branch;
+
+ /* get current branch */
+ current_branch = resolve_ref("HEAD", head_sha1, 1, NULL);
+ if (!current_branch)
+ die("No current branch");
+ if (!prefixcmp(current_branch, "refs/heads/"))
+ current_branch += 11;
+
+ /* get a line */
+ while (pos < in->len) {
+ int len;
+ char *newline, *p = in->buf + pos;
+
+ newline = strchr(p, '\n');
+ len = newline ? newline - p : strlen(p);
+ pos += len + !!newline;
+ i++;
+ p[len] = 0;
+ if (handle_line(p))
+ die ("Error in line %d: %.*s", i, len, p);
+ }
+
+ if (!srcs.nr)
+ return 0;
+
+ if (merge_title)
+ do_fmt_merge_msg_title(out, current_branch);
if (merge_summary) {
struct commit *head;
@@ -289,6 +298,9 @@ int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
rev.ignore_merges = 1;
rev.limited = 1;
+ if (suffixcmp(out->buf, "\n"))
+ strbuf_addch(out, '\n');
+
for (i = 0; i < origins.nr; i++)
shortlog(origins.items[i].string, origins.items[i].util,
head, &rev, limit, out);
@@ -296,14 +308,25 @@ int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
return 0;
}
+int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
+ return do_fmt_merge_msg(1, merge_summary, in, out);
+}
+
+int fmt_merge_msg_shortlog(struct strbuf *in, struct strbuf *out) {
+ return do_fmt_merge_msg(0, 1, in, out);
+}
+
int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
{
const char *inpath = NULL;
+ const char *message = NULL;
struct option options[] = {
OPT_BOOLEAN(0, "log", &merge_summary, "populate log with the shortlog"),
{ OPTION_BOOLEAN, 0, "summary", &merge_summary, NULL,
"alias for --log (deprecated)",
PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
+ OPT_STRING('m', "message", &message, "text",
+ "use <text> as start of message"),
OPT_FILENAME('F', "file", &inpath, "file to read from"),
OPT_END()
};
@@ -317,6 +340,12 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
0);
if (argc > 0)
usage_with_options(fmt_merge_msg_usage, options);
+ if (message && !merge_summary) {
+ char nl = '\n';
+ write_in_full(STDOUT_FILENO, message, strlen(message));
+ write_in_full(STDOUT_FILENO, &nl, 1);
+ return 0;
+ }
if (inpath && strcmp(inpath, "-")) {
in = fopen(inpath, "r");
@@ -326,7 +355,12 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
if (strbuf_read(&input, fileno(in), 0) < 0)
die_errno("could not read input file");
- ret = fmt_merge_msg(merge_summary, &input, &output);
+ if (message) {
+ strbuf_addstr(&output, message);
+ ret = fmt_merge_msg_shortlog(&input, &output);
+ } else {
+ ret = fmt_merge_msg(merge_summary, &input, &output);
+ }
if (ret)
return ret;
write_in_full(STDOUT_FILENO, output.buf, output.len);
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 62be1bbfd6..89e75c6894 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -227,6 +227,10 @@ static void grab_common_values(struct atom_value *val, int deref, struct object
strcpy(s, sha1_to_hex(obj->sha1));
v->s = s;
}
+ else if (!strcmp(name, "objectname:short")) {
+ v->s = xstrdup(find_unique_abbrev(obj->sha1,
+ DEFAULT_ABBREV));
+ }
}
}
@@ -549,10 +553,10 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, v
grab_person("committer", val, deref, obj, buf, sz);
break;
case OBJ_TREE:
- // grab_tree_values(val, deref, obj, buf, sz);
+ /* grab_tree_values(val, deref, obj, buf, sz); */
break;
case OBJ_BLOB:
- // grab_blob_values(val, deref, obj, buf, sz);
+ /* grab_blob_values(val, deref, obj, buf, sz); */
break;
default:
die("Eh? Object of type %d?", obj->type);
diff --git a/builtin/grep.c b/builtin/grep.c
index 8e928e2170..da32f3df34 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -11,14 +11,16 @@
#include "tree-walk.h"
#include "builtin.h"
#include "parse-options.h"
+#include "string-list.h"
+#include "run-command.h"
#include "userdiff.h"
#include "grep.h"
#include "quote.h"
#include "dir.h"
#ifndef NO_PTHREADS
-#include "thread-utils.h"
#include <pthread.h>
+#include "thread-utils.h"
#endif
static char const * const grep_usage[] = {
@@ -556,6 +558,33 @@ static int grep_file(struct grep_opt *opt, const char *filename)
}
}
+static void append_path(struct grep_opt *opt, const void *data, size_t len)
+{
+ struct string_list *path_list = opt->output_priv;
+
+ if (len == 1 && *(const char *)data == '\0')
+ return;
+ string_list_append(path_list, xstrndup(data, len));
+}
+
+static void run_pager(struct grep_opt *opt, const char *prefix)
+{
+ struct string_list *path_list = opt->output_priv;
+ const char **argv = xmalloc(sizeof(const char *) * (path_list->nr + 1));
+ int i, status;
+
+ for (i = 0; i < path_list->nr; i++)
+ argv[i] = path_list->items[i].string;
+ argv[path_list->nr] = NULL;
+
+ if (prefix && chdir(prefix))
+ die("Failed to chdir: %s", prefix);
+ status = run_command_v_opt(argv, RUN_USING_SHELL);
+ if (status)
+ exit(status);
+ free(argv);
+}
+
static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
{
int hit = 0;
@@ -590,7 +619,6 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
if (hit && opt->status_only)
break;
}
- free_grep_patterns(opt);
return hit;
}
@@ -675,6 +703,25 @@ static int grep_object(struct grep_opt *opt, const char **paths,
die("unable to grep from object of type %s", typename(obj->type));
}
+static int grep_objects(struct grep_opt *opt, const char **paths,
+ const struct object_array *list)
+{
+ unsigned int i;
+ int hit = 0;
+ const unsigned int nr = list->nr;
+
+ for (i = 0; i < nr; i++) {
+ struct object *real_obj;
+ real_obj = deref_tag(list->objects[i].item, NULL, 0);
+ if (grep_object(opt, paths, real_obj, list->objects[i].name)) {
+ hit = 1;
+ if (opt->status_only)
+ break;
+ }
+ }
+ return hit;
+}
+
static int grep_directory(struct grep_opt *opt, const char **paths)
{
struct dir_struct dir;
@@ -689,7 +736,6 @@ static int grep_directory(struct grep_opt *opt, const char **paths)
if (hit && opt->status_only)
break;
}
- free_grep_patterns(opt);
return hit;
}
@@ -724,11 +770,15 @@ static int file_callback(const struct option *opt, const char *arg, int unset)
if (!patterns)
die_errno("cannot open '%s'", arg);
while (strbuf_getline(&sb, patterns, '\n') == 0) {
+ char *s;
+ size_t len;
+
/* ignore empty line like grep does */
if (sb.len == 0)
continue;
- append_grep_pattern(grep_opt, strbuf_detach(&sb, NULL), arg,
- ++lno, GREP_PATTERN);
+
+ s = strbuf_detach(&sb, &len);
+ append_grep_pat(grep_opt, s, len, arg, ++lno, GREP_PATTERN);
}
fclose(patterns);
strbuf_release(&sb);
@@ -782,12 +832,14 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
int cached = 0;
int seen_dashdash = 0;
int external_grep_allowed__ignored;
+ const char *show_in_pager = NULL, *default_pager = "dummy";
struct grep_opt opt;
- struct object_array list = { 0, 0, NULL };
+ struct object_array list = OBJECT_ARRAY_INIT;
const char **paths = NULL;
+ struct string_list path_list = STRING_LIST_INIT_NODUP;
int i;
int dummy;
- int nongit = 0, use_index = 1;
+ int use_index = 1;
struct option options[] = {
OPT_BOOLEAN(0, "cached", &cached,
"search in index instead of in the work tree"),
@@ -868,6 +920,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
OPT_BOOLEAN(0, "all-match", &opt.all_match,
"show only matches from files that match all patterns"),
OPT_GROUP(""),
+ { OPTION_STRING, 'O', "open-files-in-pager", &show_in_pager,
+ "pager", "show matching files in the pager",
+ PARSE_OPT_OPTARG, NULL, (intptr_t)default_pager },
OPT_BOOLEAN(0, "ext-grep", &external_grep_allowed__ignored,
"allow calling of grep(1) (ignored by this build)"),
{ OPTION_CALLBACK, 0, "help-all", &options, NULL, "show usage",
@@ -875,8 +930,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
OPT_END()
};
- prefix = setup_git_directory_gently(&nongit);
-
/*
* 'git grep -h', unlike 'git grep -h <pattern>', is a request
* to show usage information and exit.
@@ -921,7 +974,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
PARSE_OPT_STOP_AT_NON_OPTION |
PARSE_OPT_NO_INTERNAL_HELP);
- if (use_index && nongit)
+ if (use_index && !startup_info->have_repository)
/* die the same way as if we did it at the beginning */
setup_git_directory();
@@ -943,6 +996,18 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
argc--;
}
+ if (show_in_pager == default_pager)
+ show_in_pager = git_pager(1);
+ if (show_in_pager) {
+ opt.color = 0;
+ opt.name_only = 1;
+ opt.null_following_name = 1;
+ opt.output_priv = &path_list;
+ opt.output = append_path;
+ string_list_append(&path_list, show_in_pager);
+ use_threads = 0;
+ }
+
if (!opt.pattern_list)
die("no pattern given.");
if (!opt.fixed && opt.ignore_case)
@@ -999,44 +1064,51 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
paths[1] = NULL;
}
+ if (show_in_pager && (cached || list.nr))
+ die("--open-files-in-pager only works on the worktree");
+
+ if (show_in_pager && opt.pattern_list && !opt.pattern_list->next) {
+ const char *pager = path_list.items[0].string;
+ int len = strlen(pager);
+
+ if (len > 4 && is_dir_sep(pager[len - 5]))
+ pager += len - 4;
+
+ if (!strcmp("less", pager) || !strcmp("vi", pager)) {
+ struct strbuf buf = STRBUF_INIT;
+ strbuf_addf(&buf, "+/%s%s",
+ strcmp("less", pager) ? "" : "*",
+ opt.pattern_list->pattern);
+ string_list_append(&path_list, buf.buf);
+ strbuf_detach(&buf, NULL);
+ }
+ }
+
+ if (!show_in_pager)
+ setup_pager();
+
+
if (!use_index) {
- int hit;
if (cached)
die("--cached cannot be used with --no-index.");
if (list.nr)
die("--no-index cannot be used with revs.");
hit = grep_directory(&opt, paths);
- if (use_threads)
- hit |= wait_all();
- return !hit;
- }
-
- if (!list.nr) {
- int hit;
+ } else if (!list.nr) {
if (!cached)
setup_work_tree();
hit = grep_cache(&opt, paths, cached);
- if (use_threads)
- hit |= wait_all();
- return !hit;
- }
-
- if (cached)
- die("both --cached and trees are given.");
-
- for (i = 0; i < list.nr; i++) {
- struct object *real_obj;
- real_obj = deref_tag(list.objects[i].item, NULL, 0);
- if (grep_object(&opt, paths, real_obj, list.objects[i].name)) {
- hit = 1;
- if (opt.status_only)
- break;
- }
+ } else {
+ if (cached)
+ die("both --cached and trees are given.");
+ hit = grep_objects(&opt, paths, &list);
}
if (use_threads)
hit |= wait_all();
+ if (hit && show_in_pager)
+ run_pager(&opt, prefix);
free_grep_patterns(&opt);
return !hit;
}
diff --git a/builtin/help.c b/builtin/help.c
index 3182a2bec4..61ff79839b 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -26,7 +26,7 @@ enum help_format {
HELP_FORMAT_NONE,
HELP_FORMAT_MAN,
HELP_FORMAT_INFO,
- HELP_FORMAT_WEB,
+ HELP_FORMAT_WEB
};
static int show_all = 0;
@@ -120,7 +120,7 @@ static void exec_woman_emacs(const char *path, const char *page)
if (!path)
path = "emacsclient";
strbuf_addf(&man_page, "(woman \"%s\")", page);
- execlp(path, "emacsclient", "-e", man_page.buf, NULL);
+ execlp(path, "emacsclient", "-e", man_page.buf, (char *)NULL);
warning("failed to exec '%s': %s", path, strerror(errno));
}
}
@@ -148,7 +148,7 @@ static void exec_man_konqueror(const char *path, const char *page)
} else
path = "kfmclient";
strbuf_addf(&man_page, "man:%s(1)", page);
- execlp(path, filename, "newTab", man_page.buf, NULL);
+ execlp(path, filename, "newTab", man_page.buf, (char *)NULL);
warning("failed to exec '%s': %s", path, strerror(errno));
}
}
@@ -157,7 +157,7 @@ static void exec_man_man(const char *path, const char *page)
{
if (!path)
path = "man";
- execlp(path, "man", page, NULL);
+ execlp(path, "man", page, (char *)NULL);
warning("failed to exec '%s': %s", path, strerror(errno));
}
@@ -165,7 +165,7 @@ static void exec_man_cmd(const char *cmd, const char *page)
{
struct strbuf shell_cmd = STRBUF_INIT;
strbuf_addf(&shell_cmd, "%s %s", cmd, page);
- execl("/bin/sh", "sh", "-c", shell_cmd.buf, NULL);
+ execl("/bin/sh", "sh", "-c", shell_cmd.buf, (char *)NULL);
warning("failed to exec '%s': %s", cmd, strerror(errno));
}
@@ -372,7 +372,7 @@ static void show_info_page(const char *git_cmd)
{
const char *page = cmd_to_page(git_cmd);
setenv("INFOPATH", system_path(GIT_INFO_PATH), 1);
- execlp("info", "info", "gitman", page, NULL);
+ execlp("info", "info", "gitman", page, (char *)NULL);
die("no info viewer handled the request");
}
@@ -398,7 +398,7 @@ static void get_html_page_path(struct strbuf *page_path, const char *page)
#ifndef open_html
static void open_html(const char *path)
{
- execl_git_cmd("web--browse", "-c", "help.browser", path, NULL);
+ execl_git_cmd("web--browse", "-c", "help.browser", path, (char *)NULL);
}
#endif
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index b4cf8c53e0..2e680d7a7a 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -11,7 +11,7 @@
#include "exec_cmd.h"
static const char index_pack_usage[] =
-"git index-pack [-v] [-o <index-file>] [{ ---keep | --keep=<msg> }] [--strict] { <pack-file> | --stdin [--fix-thin] [<pack-file>] }";
+"git index-pack [-v] [-o <index-file>] [{ --keep | --keep=<msg> }] [--strict] { <pack-file> | --stdin [--fix-thin] [<pack-file>] }";
struct object_entry
{
@@ -266,26 +266,23 @@ static void unlink_base_data(struct base_data *c)
static void *unpack_entry_data(unsigned long offset, unsigned long size)
{
+ int status;
z_stream stream;
void *buf = xmalloc(size);
memset(&stream, 0, sizeof(stream));
+ git_inflate_init(&stream);
stream.next_out = buf;
stream.avail_out = size;
- stream.next_in = fill(1);
- stream.avail_in = input_len;
- git_inflate_init(&stream);
- for (;;) {
- int ret = git_inflate(&stream, 0);
- use(input_len - stream.avail_in);
- if (stream.total_out == size && ret == Z_STREAM_END)
- break;
- if (ret != Z_OK)
- bad_object(offset, "inflate returned %d", ret);
+ do {
stream.next_in = fill(1);
stream.avail_in = input_len;
- }
+ status = git_inflate(&stream, 0);
+ use(input_len - stream.avail_in);
+ } while (status == Z_OK);
+ if (stream.total_out != size || status != Z_STREAM_END)
+ bad_object(offset, "inflate returned %d", status);
git_inflate_end(&stream);
return buf;
}
@@ -359,34 +356,38 @@ static void *get_data_from_pack(struct object_entry *obj)
{
off_t from = obj[0].idx.offset + obj[0].hdr_size;
unsigned long len = obj[1].idx.offset - from;
- unsigned long rdy = 0;
- unsigned char *src, *data;
+ unsigned char *data, *inbuf;
z_stream stream;
- int st;
+ int status;
- src = xmalloc(len);
- data = src;
- do {
- ssize_t n = pread(pack_fd, data + rdy, len - rdy, from + rdy);
- if (n < 0)
- die_errno("cannot pread pack file");
- if (!n)
- die("premature end of pack file, %lu bytes missing",
- len - rdy);
- rdy += n;
- } while (rdy < len);
data = xmalloc(obj->size);
+ inbuf = xmalloc((len < 64*1024) ? len : 64*1024);
+
memset(&stream, 0, sizeof(stream));
+ git_inflate_init(&stream);
stream.next_out = data;
stream.avail_out = obj->size;
- stream.next_in = src;
- stream.avail_in = len;
- git_inflate_init(&stream);
- while ((st = git_inflate(&stream, Z_FINISH)) == Z_OK);
- git_inflate_end(&stream);
- if (st != Z_STREAM_END || stream.total_out != obj->size)
+
+ do {
+ ssize_t n = (len < 64*1024) ? len : 64*1024;
+ n = pread(pack_fd, inbuf, n, from);
+ if (n < 0)
+ die_errno("cannot pread pack file");
+ if (!n)
+ die("premature end of pack file, %lu bytes missing", len);
+ from += n;
+ len -= n;
+ stream.next_in = inbuf;
+ stream.avail_in = n;
+ status = git_inflate(&stream, 0);
+ } while (len && status == Z_OK && !stream.avail_in);
+
+ /* This has been inflated OK when first encountered, so... */
+ if (status != Z_STREAM_END || stream.total_out != obj->size)
die("serious inflate inconsistency");
- free(src);
+
+ git_inflate_end(&stream);
+ free(inbuf);
return data;
}
@@ -668,25 +669,25 @@ static void parse_pack_objects(unsigned char *sha1)
static int write_compressed(struct sha1file *f, void *in, unsigned int size)
{
z_stream stream;
- unsigned long maxsize;
- void *out;
+ int status;
+ unsigned char outbuf[4096];
memset(&stream, 0, sizeof(stream));
deflateInit(&stream, zlib_compression_level);
- maxsize = deflateBound(&stream, size);
- out = xmalloc(maxsize);
-
- /* Compress it */
stream.next_in = in;
stream.avail_in = size;
- stream.next_out = out;
- stream.avail_out = maxsize;
- while (deflate(&stream, Z_FINISH) == Z_OK);
- deflateEnd(&stream);
+ do {
+ stream.next_out = outbuf;
+ stream.avail_out = sizeof(outbuf);
+ status = deflate(&stream, Z_FINISH);
+ sha1write(f, outbuf, sizeof(outbuf) - stream.avail_out);
+ } while (status == Z_OK);
+
+ if (status != Z_STREAM_END)
+ die("unable to deflate appended object (%d)", status);
size = stream.total_out;
- sha1write(f, out, size);
- free(out);
+ deflateEnd(&stream);
return size;
}
@@ -883,25 +884,11 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
if (argc == 2 && !strcmp(argv[1], "-h"))
usage(index_pack_usage);
- /*
- * We wish to read the repository's config file if any, and
- * for that it is necessary to call setup_git_directory_gently().
- * However if the cwd was inside .git/objects/pack/ then we need
- * to go back there or all the pack name arguments will be wrong.
- * And in that case we cannot rely on any prefix returned by
- * setup_git_directory_gently() either.
- */
- {
- char cwd[PATH_MAX+1];
- int nongit;
-
- if (!getcwd(cwd, sizeof(cwd)-1))
- die("Unable to get current working directory");
- setup_git_directory_gently(&nongit);
- git_config(git_index_pack_config, NULL);
- if (chdir(cwd))
- die("Cannot come back to cwd");
- }
+ read_replace_refs = 0;
+
+ git_config(git_index_pack_config, NULL);
+ if (prefix && chdir(prefix))
+ die("Cannot come back to cwd");
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
diff --git a/builtin/init-db.c b/builtin/init-db.c
index edc40ff574..0271285fad 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -463,7 +463,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
static char git_dir[PATH_MAX+1];
setenv(GIT_DIR_ENVIRONMENT,
- getcwd(git_dir, sizeof(git_dir)), 0);
+ getcwd(git_dir, sizeof(git_dir)), argc > 0);
}
if (init_shared_repository != -1)
diff --git a/builtin/log.c b/builtin/log.c
index 6208703c06..eaa1ee0fa7 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -24,6 +24,7 @@
static const char *default_date_mode = NULL;
static int default_show_root = 1;
+static int decoration_style;
static const char *fmt_patch_subject_prefix = "PATCH";
static const char *fmt_pretty;
@@ -31,11 +32,28 @@ static const char * const builtin_log_usage =
"git log [<options>] [<since>..<until>] [[--] <path>...]\n"
" or: git show [options] <object>...";
+static int parse_decoration_style(const char *var, const char *value)
+{
+ switch (git_config_maybe_bool(var, value)) {
+ case 1:
+ return DECORATE_SHORT_REFS;
+ case 0:
+ return 0;
+ default:
+ break;
+ }
+ if (!strcmp(value, "full"))
+ return DECORATE_FULL_REFS;
+ else if (!strcmp(value, "short"))
+ return DECORATE_SHORT_REFS;
+ return -1;
+}
+
static void cmd_log_init(int argc, const char **argv, const char *prefix,
struct rev_info *rev, struct setup_revision_opt *opt)
{
int i;
- int decoration_style = 0;
+ int decoration_given = 0;
struct userformat_want w;
rev->abbrev = DEFAULT_ABBREV;
@@ -78,14 +96,15 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
const char *arg = argv[i];
if (!strcmp(arg, "--decorate")) {
decoration_style = DECORATE_SHORT_REFS;
+ decoration_given = 1;
} else if (!prefixcmp(arg, "--decorate=")) {
const char *v = skip_prefix(arg, "--decorate=");
- if (!strcmp(v, "full"))
- decoration_style = DECORATE_FULL_REFS;
- else if (!strcmp(v, "short"))
- decoration_style = DECORATE_SHORT_REFS;
- else
+ decoration_style = parse_decoration_style(arg, v);
+ if (decoration_style < 0)
die("invalid --decorate option: %s", arg);
+ decoration_given = 1;
+ } else if (!strcmp(arg, "--no-decorate")) {
+ decoration_style = 0;
} else if (!strcmp(arg, "--source")) {
rev->show_source = 1;
} else if (!strcmp(arg, "-h")) {
@@ -93,10 +112,20 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
} else
die("unrecognized argument: %s", arg);
}
+
+ /*
+ * defeat log.decorate configuration interacting with --pretty=raw
+ * from the command line.
+ */
+ if (!decoration_given && rev->pretty_given
+ && rev->commit_format == CMIT_FMT_RAW)
+ decoration_style = 0;
+
if (decoration_style) {
rev->show_decorations = 1;
load_ref_decorations(decoration_style);
}
+ setup_pager();
}
/*
@@ -258,10 +287,19 @@ static int git_log_config(const char *var, const char *value, void *cb)
return git_config_string(&fmt_patch_subject_prefix, var, value);
if (!strcmp(var, "log.date"))
return git_config_string(&default_date_mode, var, value);
+ if (!strcmp(var, "log.decorate")) {
+ decoration_style = parse_decoration_style(var, value);
+ if (decoration_style < 0)
+ decoration_style = 0; /* maybe warn? */
+ return 0;
+ }
if (!strcmp(var, "log.showroot")) {
default_show_root = git_config_bool(var, value);
return 0;
}
+ if (!prefixcmp(var, "color.decorate."))
+ return parse_decorate_color_config(var, 15, value);
+
return git_diff_ui_config(var, value, cb);
}
@@ -454,12 +492,6 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
rev.use_terminator = 1;
rev.always_show_header = 1;
- /*
- * We get called through "git reflog", so unlike the other log
- * routines, we need to set up our pager manually..
- */
- setup_pager();
-
return cmd_log_walk(&rev);
}
@@ -501,13 +533,13 @@ static void add_header(const char *value)
len--;
if (!strncasecmp(value, "to: ", 4)) {
- item = string_list_append(value + 4, &extra_to);
+ item = string_list_append(&extra_to, value + 4);
len -= 4;
} else if (!strncasecmp(value, "cc: ", 4)) {
- item = string_list_append(value + 4, &extra_cc);
+ item = string_list_append(&extra_cc, value + 4);
len -= 4;
} else {
- item = string_list_append(value, &extra_hdr);
+ item = string_list_append(&extra_hdr, value);
}
item->string[len] = '\0';
@@ -515,8 +547,9 @@ static void add_header(const char *value)
#define THREAD_SHALLOW 1
#define THREAD_DEEP 2
-static int thread = 0;
-static int do_signoff = 0;
+static int thread;
+static int do_signoff;
+static const char *signature = git_version_string;
static int git_format_config(const char *var, const char *value, void *cb)
{
@@ -531,13 +564,13 @@ static int git_format_config(const char *var, const char *value, void *cb)
if (!strcmp(var, "format.to")) {
if (!value)
return config_error_nonbool(var);
- string_list_append(value, &extra_to);
+ string_list_append(&extra_to, value);
return 0;
}
if (!strcmp(var, "format.cc")) {
if (!value)
return config_error_nonbool(var);
- string_list_append(value, &extra_cc);
+ string_list_append(&extra_cc, value);
return 0;
}
if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
@@ -575,6 +608,8 @@ static int git_format_config(const char *var, const char *value, void *cb)
do_signoff = git_config_bool(var, value);
return 0;
}
+ if (!strcmp(var, "format.signature"))
+ return git_config_string(&signature, var, value);
return git_log_config(var, value, cb);
}
@@ -669,6 +704,12 @@ static void gen_message_id(struct rev_info *info, char *base)
info->message_id = strbuf_detach(&buf, NULL);
}
+static void print_signature(void)
+{
+ if (signature && *signature)
+ printf("-- \n%s\n\n", signature);
+}
+
static void make_cover_letter(struct rev_info *rev, int use_stdout,
int numbered, int numbered_files,
struct commit *origin,
@@ -762,6 +803,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
diff_flush(&opts);
printf("\n");
+ print_signature();
}
static const char *clean_message_id(const char *msg_id)
@@ -915,7 +957,7 @@ static int to_callback(const struct option *opt, const char *arg, int unset)
if (unset)
string_list_clear(&extra_to, 0);
else
- string_list_append(arg, &extra_to);
+ string_list_append(&extra_to, arg);
return 0;
}
@@ -924,7 +966,7 @@ static int cc_callback(const struct option *opt, const char *arg, int unset)
if (unset)
string_list_clear(&extra_cc, 0);
else
- string_list_append(arg, &extra_cc);
+ string_list_append(&extra_cc, arg);
return 0;
}
@@ -1001,6 +1043,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
{ OPTION_CALLBACK, 0, "thread", &thread, "style",
"enable message threading, styles: shallow, deep",
PARSE_OPT_OPTARG, thread_callback },
+ OPT_STRING(0, "signature", &signature, "signature",
+ "add a signature"),
OPT_END()
};
@@ -1205,7 +1249,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
rev.ref_message_ids = xcalloc(1, sizeof(struct string_list));
if (in_reply_to) {
const char *msgid = clean_message_id(in_reply_to);
- string_list_append(msgid, rev.ref_message_ids);
+ string_list_append(rev.ref_message_ids, msgid);
}
rev.numbered_files = numbered_files;
rev.patch_suffix = fmt_patch_suffix;
@@ -1252,8 +1296,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
&& (!cover_letter || rev.nr > 1))
free(rev.message_id);
else
- string_list_append(rev.message_id,
- rev.ref_message_ids);
+ string_list_append(rev.ref_message_ids,
+ rev.message_id);
}
gen_message_id(&rev, sha1_to_hex(commit->object.sha1));
}
@@ -1279,7 +1323,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
mime_boundary_leader,
rev.mime_boundary);
else
- printf("-- \n%s\n\n", git_version_string);
+ print_signature();
}
if (!use_stdout)
fclose(stdout);
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index c0fbcdcf4f..bb4f612b3d 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -25,9 +25,11 @@ static int show_modified;
static int show_killed;
static int show_valid_bit;
static int line_terminator = '\n';
+static int debug_mode;
+static const char *prefix;
+static int max_prefix_len;
static int prefix_len;
-static int prefix_offset;
static const char **pathspec;
static int error_unmatch;
static char *ps_matched;
@@ -43,10 +45,15 @@ static const char *tag_modified = "";
static const char *tag_skip_worktree = "";
static const char *tag_resolve_undo = "";
+static void write_name(const char* name, size_t len)
+{
+ write_name_quoted_relative(name, len, prefix, prefix_len, stdout,
+ line_terminator);
+}
+
static void show_dir_entry(const char *tag, struct dir_entry *ent)
{
- int len = prefix_len;
- int offset = prefix_offset;
+ int len = max_prefix_len;
if (len >= ent->len)
die("git ls-files: internal error - directory entry not superset of prefix");
@@ -55,7 +62,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)
return;
fputs(tag, stdout);
- write_name_quoted(ent->name + offset, stdout, line_terminator);
+ write_name(ent->name, ent->len);
}
static void show_other_files(struct dir_struct *dir)
@@ -121,8 +128,7 @@ static void show_killed_files(struct dir_struct *dir)
static void show_ce_entry(const char *tag, struct cache_entry *ce)
{
- int len = prefix_len;
- int offset = prefix_offset;
+ int len = max_prefix_len;
if (len >= ce_namelen(ce))
die("git ls-files: internal error - cache entry not superset of prefix");
@@ -156,40 +162,45 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
find_unique_abbrev(ce->sha1,abbrev),
ce_stage(ce));
}
- write_name_quoted(ce->name + offset, stdout, line_terminator);
-}
-
-static int show_one_ru(struct string_list_item *item, void *cbdata)
-{
- int offset = prefix_offset;
- const char *path = item->string;
- struct resolve_undo_info *ui = item->util;
- int i, len;
-
- len = strlen(path);
- if (len < prefix_len)
- return 0; /* outside of the prefix */
- if (!match_pathspec(pathspec, path, len, prefix_len, ps_matched))
- return 0; /* uninterested */
- for (i = 0; i < 3; i++) {
- if (!ui->mode[i])
- continue;
- printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
- find_unique_abbrev(ui->sha1[i], abbrev),
- i + 1);
- write_name_quoted(path + offset, stdout, line_terminator);
+ write_name(ce->name, ce_namelen(ce));
+ if (debug_mode) {
+ printf(" ctime: %d:%d\n", ce->ce_ctime.sec, ce->ce_ctime.nsec);
+ printf(" mtime: %d:%d\n", ce->ce_mtime.sec, ce->ce_mtime.nsec);
+ printf(" dev: %d\tino: %d\n", ce->ce_dev, ce->ce_ino);
+ printf(" uid: %d\tgid: %d\n", ce->ce_uid, ce->ce_gid);
+ printf(" size: %d\tflags: %x\n", ce->ce_size, ce->ce_flags);
}
- return 0;
}
-static void show_ru_info(const char *prefix)
+static void show_ru_info(void)
{
+ struct string_list_item *item;
+
if (!the_index.resolve_undo)
return;
- for_each_string_list(show_one_ru, the_index.resolve_undo, NULL);
+
+ for_each_string_list_item(item, the_index.resolve_undo) {
+ const char *path = item->string;
+ struct resolve_undo_info *ui = item->util;
+ int i, len;
+
+ len = strlen(path);
+ if (len < max_prefix_len)
+ continue; /* outside of the prefix */
+ if (!match_pathspec(pathspec, path, len, max_prefix_len, ps_matched))
+ continue; /* uninterested */
+ for (i = 0; i < 3; i++) {
+ if (!ui->mode[i])
+ continue;
+ printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
+ find_unique_abbrev(ui->sha1[i], abbrev),
+ i + 1);
+ write_name(path, len);
+ }
+ }
}
-static void show_files(struct dir_struct *dir, const char *prefix)
+static void show_files(struct dir_struct *dir)
{
int i;
@@ -243,7 +254,7 @@ static void show_files(struct dir_struct *dir, const char *prefix)
*/
static void prune_cache(const char *prefix)
{
- int pos = cache_name_pos(prefix, prefix_len);
+ int pos = cache_name_pos(prefix, max_prefix_len);
unsigned int first, last;
if (pos < 0)
@@ -256,7 +267,7 @@ static void prune_cache(const char *prefix)
while (last > first) {
int next = (last + first) >> 1;
struct cache_entry *ce = active_cache[next];
- if (!strncmp(ce->name, prefix, prefix_len)) {
+ if (!strncmp(ce->name, prefix, max_prefix_len)) {
first = next+1;
continue;
}
@@ -265,11 +276,16 @@ static void prune_cache(const char *prefix)
active_nr = last;
}
-static const char *verify_pathspec(const char *prefix)
+static const char *pathspec_prefix(const char *prefix)
{
const char **p, *n, *prev;
unsigned long max;
+ if (!pathspec) {
+ max_prefix_len = prefix ? strlen(prefix) : 0;
+ return prefix;
+ }
+
prev = NULL;
max = PATH_MAX;
for (p = pathspec; (n = *p) != NULL; p++) {
@@ -291,10 +307,7 @@ static const char *verify_pathspec(const char *prefix)
}
}
- if (prefix_offset > max || memcmp(prev, prefix, prefix_offset))
- die("git ls-files: cannot generate relative filenames containing '..'");
-
- prefix_len = max;
+ max_prefix_len = max;
return max ? xmemdupz(prev, max) : NULL;
}
@@ -374,7 +387,7 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix)
}
}
-int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset)
+int report_path_error(const char *ps_matched, const char **pathspec, int prefix_len)
{
/*
* Make sure all pathspec matched; otherwise it is an error.
@@ -404,7 +417,7 @@ int report_path_error(const char *ps_matched, const char **pathspec, int prefix_
continue;
error("pathspec '%s' did not match any file(s) known to git.",
- pathspec[num] + prefix_offset);
+ pathspec[num] + prefix_len);
errors++;
}
return errors;
@@ -456,9 +469,10 @@ static int option_parse_exclude_standard(const struct option *opt,
return 0;
}
-int cmd_ls_files(int argc, const char **argv, const char *prefix)
+int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
{
int require_work_tree = 0, show_tag = 0;
+ const char *max_prefix;
struct dir_struct dir;
struct option builtin_ls_files_options[] = {
{ OPTION_CALLBACK, 'z', NULL, NULL, NULL,
@@ -504,7 +518,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
{ OPTION_CALLBACK, 0, "exclude-standard", &dir, NULL,
"add the standard git exclusions",
PARSE_OPT_NOARG, option_parse_exclude_standard },
- { OPTION_SET_INT, 0, "full-name", &prefix_offset, NULL,
+ { OPTION_SET_INT, 0, "full-name", &prefix_len, NULL,
"make the output relative to the project top directory",
PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL },
OPT_BOOLEAN(0, "error-unmatch", &error_unmatch,
@@ -512,12 +526,14 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
OPT_STRING(0, "with-tree", &with_tree, "tree-ish",
"pretend that paths removed since <tree-ish> are still present"),
OPT__ABBREV(&abbrev),
+ OPT_BOOLEAN(0, "debug", &debug_mode, "show debugging data"),
OPT_END()
};
memset(&dir, 0, sizeof(dir));
+ prefix = cmd_prefix;
if (prefix)
- prefix_offset = strlen(prefix);
+ prefix_len = strlen(prefix);
git_config(git_default_config, NULL);
if (read_cache() < 0)
@@ -555,9 +571,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
if (pathspec)
strip_trailing_slash_from_submodules();
- /* Verify that the pathspec matches the prefix */
- if (pathspec)
- prefix = verify_pathspec(prefix);
+ /* Find common prefix for all pathspec's */
+ max_prefix = pathspec_prefix(prefix);
/* Treat unmatching pathspec elements as errors */
if (pathspec && error_unmatch) {
@@ -575,8 +590,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
show_killed | show_modified | show_resolve_undo))
show_cached = 1;
- if (prefix)
- prune_cache(prefix);
+ if (max_prefix)
+ prune_cache(max_prefix);
if (with_tree) {
/*
* Basic sanity check; show-stages and show-unmerged
@@ -584,15 +599,15 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
*/
if (show_stage || show_unmerged)
die("ls-files --with-tree is incompatible with -s or -u");
- overlay_tree_on_cache(with_tree, prefix);
+ overlay_tree_on_cache(with_tree, max_prefix);
}
- show_files(&dir, prefix);
+ show_files(&dir);
if (show_resolve_undo)
- show_ru_info(prefix);
+ show_ru_info();
if (ps_matched) {
int bad;
- bad = report_path_error(ps_matched, pathspec, prefix_offset);
+ bad = report_path_error(ps_matched, pathspec, prefix_len);
if (bad)
fprintf(stderr, "Did you forget to 'git add'?\n");
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index 70f5622d9d..97eed4012b 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -4,7 +4,8 @@
#include "remote.h"
static const char ls_remote_usage[] =
-"git ls-remote [--heads] [--tags] [-u <exec> | --upload-pack <exec>] <repository> <refs>...";
+"git ls-remote [--heads] [--tags] [-u <exec> | --upload-pack <exec>]\n"
+" [-q|--quiet] [<repository> [<refs>...]]";
/*
* Is there one among the list of patterns that match the tail part
@@ -31,8 +32,8 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
{
int i;
const char *dest = NULL;
- int nongit;
unsigned flags = 0;
+ int quiet = 0;
const char *uploadpack = NULL;
const char **pattern = NULL;
@@ -40,8 +41,6 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
struct transport *transport;
const struct ref *ref;
- setup_git_directory_gently(&nongit);
-
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
@@ -66,6 +65,10 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
flags |= REF_NORMAL;
continue;
}
+ if (!strcmp("--quiet", arg) || !strcmp("-q", arg)) {
+ quiet = 1;
+ continue;
+ }
usage(ls_remote_usage);
}
dest = arg;
@@ -73,9 +76,6 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
break;
}
- if (!dest)
- usage(ls_remote_usage);
-
if (argv[i]) {
int j;
pattern = xcalloc(sizeof(const char *), argc - i + 1);
@@ -87,6 +87,11 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
}
}
remote = remote_get(dest);
+ if (!remote) {
+ if (dest)
+ die("bad repository '%s'", dest);
+ die("No remote configured to list refs from.");
+ }
if (!remote->url_nr)
die("remote %s has no configured URL", dest);
transport = transport_get(remote, NULL);
@@ -96,6 +101,9 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
ref = transport_get_remote_refs(transport);
if (transport_disconnect(transport))
return 1;
+
+ if (!dest && !quiet)
+ fprintf(stderr, "From %s\n", *remote->url);
for ( ; ref; ref = ref->next) {
if (!check_ref_type(ref, flags))
continue;
diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 4a9729b9b3..2320d981ce 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -17,10 +17,10 @@ static struct strbuf name = STRBUF_INIT;
static struct strbuf email = STRBUF_INIT;
static enum {
- TE_DONTCARE, TE_QP, TE_BASE64,
+ TE_DONTCARE, TE_QP, TE_BASE64
} transfer_encoding;
static enum {
- TYPE_TEXT, TYPE_OTHER,
+ TYPE_TEXT, TYPE_OTHER
} message_type;
static struct strbuf charset = STRBUF_INIT;
diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c
index cdfc1b7042..99654d0222 100644
--- a/builtin/mailsplit.c
+++ b/builtin/mailsplit.c
@@ -121,7 +121,7 @@ static int populate_maildir_list(struct string_list *list, const char *path)
if (dent->d_name[0] == '.')
continue;
snprintf(name, sizeof(name), "%s/%s", *sub, dent->d_name);
- string_list_insert(name, list);
+ string_list_insert(list, name);
}
closedir(dir);
@@ -137,7 +137,7 @@ static int split_maildir(const char *maildir, const char *dir,
char name[PATH_MAX];
int ret = -1;
int i;
- struct string_list list = {NULL, 0, 0, 1};
+ struct string_list list = STRING_LIST_INIT_DUP;
if (populate_maildir_list(&list, maildir) < 0)
goto out;
diff --git a/builtin/merge-base.c b/builtin/merge-base.c
index 54e7ec2237..96dd160731 100644
--- a/builtin/merge-base.c
+++ b/builtin/merge-base.c
@@ -23,7 +23,8 @@ static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
}
static const char * const merge_base_usage[] = {
- "git merge-base [-a|--all] <commit> <commit>...",
+ "git merge-base [-a|--all] [--octopus] <commit> <commit>...",
+ "git merge-base --independent <commit>...",
NULL
};
@@ -41,21 +42,58 @@ static struct commit *get_commit_reference(const char *arg)
return r;
}
+static int handle_octopus(int count, const char **args, int reduce, int show_all)
+{
+ struct commit_list *revs = NULL;
+ struct commit_list *result;
+ int i;
+
+ if (reduce)
+ show_all = 1;
+
+ for (i = count - 1; i >= 0; i--)
+ commit_list_insert(get_commit_reference(args[i]), &revs);
+
+ result = reduce ? reduce_heads(revs) : get_octopus_merge_bases(revs);
+
+ if (!result)
+ return 1;
+
+ while (result) {
+ printf("%s\n", sha1_to_hex(result->item->object.sha1));
+ if (!show_all)
+ return 0;
+ result = result->next;
+ }
+
+ return 0;
+}
+
int cmd_merge_base(int argc, const char **argv, const char *prefix)
{
struct commit **rev;
int rev_nr = 0;
int show_all = 0;
+ int octopus = 0;
+ int reduce = 0;
struct option options[] = {
- OPT_BOOLEAN('a', "all", &show_all, "outputs all common ancestors"),
+ OPT_BOOLEAN('a', "all", &show_all, "output all common ancestors"),
+ OPT_BOOLEAN(0, "octopus", &octopus, "find ancestors for a single n-way merge"),
+ OPT_BOOLEAN(0, "independent", &reduce, "list revs not reachable from others"),
OPT_END()
};
git_config(git_default_config, NULL);
argc = parse_options(argc, argv, prefix, options, merge_base_usage, 0);
- if (argc < 2)
+ if (!octopus && !reduce && argc < 2)
usage_with_options(merge_base_usage, options);
+ if (reduce && (show_all || octopus))
+ die("--independent cannot be used with other options");
+
+ if (octopus || reduce)
+ return handle_octopus(argc, argv, reduce, show_all);
+
rev = xmalloc(argc * sizeof(*rev));
while (argc-- > 0)
rev[rev_nr++] = get_commit_reference(*argv++);
diff --git a/builtin/merge-file.c b/builtin/merge-file.c
index 610849a653..b6664d49be 100644
--- a/builtin/merge-file.c
+++ b/builtin/merge-file.c
@@ -25,10 +25,9 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
const char *names[3] = { NULL, NULL, NULL };
mmfile_t mmfs[3];
mmbuffer_t result = {NULL, 0};
- xmparam_t xmp = {{XDF_NEED_MINIMAL}};
+ xmparam_t xmp = {{0}};
int ret = 0, i = 0, to_stdout = 0;
int quiet = 0;
- int nongit;
struct option options[] = {
OPT_BOOLEAN('p', "stdout", &to_stdout, "send results to standard output"),
OPT_SET_INT(0, "diff3", &xmp.style, "use a diff3 based merge", XDL_MERGE_DIFF3),
@@ -50,8 +49,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
xmp.style = 0;
xmp.favor = 0;
- prefix = setup_git_directory_gently(&nongit);
- if (!nongit) {
+ if (startup_info->have_repository) {
/* Read the configuration file */
git_config(git_xmerge_config, NULL);
if (0 <= git_xmerge_style)
diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c
index d8875d5892..78b9db76a0 100644
--- a/builtin/merge-recursive.c
+++ b/builtin/merge-recursive.c
@@ -3,6 +3,9 @@
#include "tag.h"
#include "merge-recursive.h"
+static const char builtin_merge_recursive_usage[] =
+ "git %s <base>... -- <head> <remote> ...";
+
static const char *better_branch_name(const char *branch)
{
static char githead_env[8 + 40 + 1];
@@ -29,7 +32,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
o.subtree_shift = "";
if (argc < 4)
- usagef("%s <base>... -- <head> <remote> ...", argv[0]);
+ usagef(builtin_merge_recursive_usage, argv[0]);
for (i = 1; i < argc; ++i) {
const char *arg = argv[i];
@@ -45,6 +48,10 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
o.subtree_shift = "";
else if (!prefixcmp(arg+2, "subtree="))
o.subtree_shift = arg + 10;
+ else if (!strcmp(arg+2, "renormalize"))
+ o.renormalize = 1;
+ else if (!strcmp(arg+2, "no-renormalize"))
+ o.renormalize = 0;
else
die("Unknown option %s", arg);
continue;
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index a4a4f2ce4c..9b25ddc979 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -60,6 +60,7 @@ static void *result(struct merge_list *entry, unsigned long *size)
{
enum object_type type;
struct blob *base, *our, *their;
+ const char *path = entry->path;
if (!entry->stage)
return read_sha1_file(entry->blob->object.sha1, &type, size);
@@ -76,7 +77,7 @@ static void *result(struct merge_list *entry, unsigned long *size)
their = NULL;
if (entry)
their = entry->blob;
- return merge_file(entry->path, base, our, their, size);
+ return merge_file(path, base, our, their, size);
}
static void *origin(struct merge_list *entry, unsigned long *size)
@@ -106,7 +107,7 @@ static void show_diff(struct merge_list *entry)
xdemitconf_t xecfg;
xdemitcb_t ecb;
- xpp.flags = XDF_NEED_MINIMAL;
+ xpp.flags = 0;
memset(&xecfg, 0, sizeof(xecfg));
xecfg.ctxlen = 3;
ecb.outf = show_outf;
diff --git a/builtin/merge.c b/builtin/merge.c
index c043066845..5f65c0c8a6 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -54,6 +54,7 @@ static size_t use_strategies_nr, use_strategies_alloc;
static const char **xopts;
static size_t xopts_nr, xopts_alloc;
static const char *branch;
+static int option_renormalize;
static int verbosity;
static int allow_rerere_auto;
@@ -131,6 +132,7 @@ static struct strategy *get_strategy(const char *name)
ret = xcalloc(1, sizeof(struct strategy));
ret->name = xstrdup(name);
+ ret->attr = NO_TRIVIAL;
return ret;
}
@@ -437,7 +439,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
strbuf_addstr(&truname, "refs/heads/");
strbuf_addstr(&truname, remote);
strbuf_setlen(&truname, truname.len - len);
- if (resolve_ref(truname.buf, buf_sha, 0, NULL)) {
+ if (resolve_ref(truname.buf, buf_sha, 1, NULL)) {
strbuf_addf(msg,
"%s\t\tbranch '%s'%s of .\n",
sha1_to_hex(remote_head->sha1),
@@ -486,7 +488,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)
buf = xstrdup(v);
argc = split_cmdline(buf, &argv);
if (argc < 0)
- die("Bad branch.%s.mergeoptions string", branch);
+ die("Bad branch.%s.mergeoptions string: %s", branch,
+ split_cmdline_strerror(argc));
argv = xrealloc(argv, sizeof(*argv) * (argc + 2));
memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
argc++;
@@ -503,6 +506,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)
return git_config_string(&pull_octopus, k, v);
else if (!strcmp(k, "merge.log") || !strcmp(k, "merge.summary"))
option_log = git_config_bool(k, v);
+ else if (!strcmp(k, "merge.renormalize"))
+ option_renormalize = git_config_bool(k, v);
return git_diff_ui_config(k, v, cb);
}
@@ -548,13 +553,53 @@ static void write_tree_trivial(unsigned char *sha1)
die("git write-tree failed to write a tree");
}
-static int try_merge_strategy(const char *strategy, struct commit_list *common,
- const char *head_arg)
+int try_merge_command(const char *strategy, 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(sha1_to_hex(j->item->object.sha1));
+ args[i++] = "--";
+ args[i++] = head_arg;
+ for (j = remotes; j; j = j->next)
+ args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
+ 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,
+ const char *head_arg)
+{
int index_fd;
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
@@ -567,12 +612,13 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
rollback_lock_file(lock);
if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree")) {
- int clean;
+ int clean, x;
struct commit *result;
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
int index_fd;
struct commit_list *reversed = NULL;
struct merge_options o;
+ struct commit_list *j;
if (remoteheads->next) {
error("Not handling anything other than two heads merge.");
@@ -583,6 +629,11 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
if (!strcmp(strategy, "subtree"))
o.subtree_shift = "";
+ o.renormalize = option_renormalize;
+
+ /*
+ * NEEDSWORK: merge with table in builtin/merge-recursive
+ */
for (x = 0; x < xopts_nr; x++) {
if (!strcmp(xopts[x], "ours"))
o.recursive_variant = MERGE_RECURSIVE_OURS;
@@ -592,6 +643,10 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
o.subtree_shift = "";
else if (!prefixcmp(xopts[x], "subtree="))
o.subtree_shift = xopts[x]+8;
+ else if (!strcmp(xopts[x], "renormalize"))
+ o.renormalize = 1;
+ else if (!strcmp(xopts[x], "no-renormalize"))
+ o.renormalize = 0;
else
die("Unknown option for merge-recursive: -X%s", xopts[x]);
}
@@ -612,39 +667,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
rollback_lock_file(lock);
return clean ? 0 : 1;
} else {
- args = xmalloc((4 + xopts_nr + commit_list_count(common) +
- commit_list_count(remoteheads)) * 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(sha1_to_hex(j->item->object.sha1));
- args[i++] = "--";
- args[i++] = head_arg;
- for (j = remoteheads; j; j = j->next)
- args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
- 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 = remoteheads; 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;
+ return try_merge_command(strategy, common, head_arg, remoteheads);
}
}
@@ -695,7 +718,7 @@ int checkout_fast_forward(const unsigned char *head, const unsigned char *remote
opts.verbose_update = 1;
opts.merge = 1;
opts.fn = twoway_merge;
- opts.msgs = get_porcelain_error_msgs();
+ setup_unpack_trees_porcelain(&opts, "merge");
trees[nr_trees] = parse_tree_indirect(head);
if (!trees[nr_trees++])
@@ -807,7 +830,7 @@ static int finish_automerge(struct commit_list *common,
return 0;
}
-static int suggest_conflicts(void)
+static int suggest_conflicts(int renormalizing)
{
FILE *fp;
int pos;
@@ -973,7 +996,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
reset_hard(remote_head->sha1, 0);
return 0;
} else {
- struct strbuf msg = STRBUF_INIT;
+ struct strbuf merge_names = STRBUF_INIT;
/* We are invoked directly as the first-class UI. */
head_arg = "HEAD";
@@ -986,13 +1009,17 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* codepath so we discard the error in this
* loop.
*/
- if (!have_message) {
- for (i = 0; i < argc; i++)
- merge_name(argv[i], &msg);
- fmt_merge_msg(option_log, &msg, &merge_msg);
- if (merge_msg.len)
- strbuf_setlen(&merge_msg, merge_msg.len-1);
- }
+ for (i = 0; i < argc; i++)
+ merge_name(argv[i], &merge_names);
+
+ if (have_message && option_log)
+ fmt_merge_msg_shortlog(&merge_names, &merge_msg);
+ else if (!have_message)
+ fmt_merge_msg(option_log, &merge_names, &merge_msg);
+
+
+ if (!(have_message && !option_log) && merge_msg.len)
+ strbuf_setlen(&merge_msg, merge_msg.len-1);
}
if (head_invalid || !argc)
@@ -1288,5 +1315,5 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
"stopped before committing as requested\n");
return 0;
} else
- return suggest_conflicts();
+ return suggest_conflicts(option_renormalize);
}
diff --git a/builtin/mv.c b/builtin/mv.c
index c07f53b343..cdbb09473c 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -63,7 +63,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
const char **source, **destination, **dest_path;
enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes;
struct stat st;
- struct string_list src_for_dst = {NULL, 0, 0, 0};
+ struct string_list src_for_dst = STRING_LIST_INIT_NODUP;
git_config(git_default_config, NULL);
@@ -180,7 +180,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
} else if (string_list_has_string(&src_for_dst, dst))
bad = "multiple sources for the same target";
else
- string_list_insert(dst, &src_for_dst);
+ string_list_insert(&src_for_dst, dst);
if (bad) {
if (ignore_errors) {
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 06a38ac8c1..31f5c1c971 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -220,7 +220,7 @@ static void name_rev_line(char *p, struct name_ref_data *data)
int cmd_name_rev(int argc, const char **argv, const char *prefix)
{
- struct object_array revs = { 0, 0, NULL };
+ struct object_array revs = OBJECT_ARRAY_INIT;
int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0;
struct name_ref_data data = { 0, 0, NULL };
struct option opts[] = {
diff --git a/builtin/notes.c b/builtin/notes.c
index 52b72fca68..fbc347c9f0 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -26,7 +26,7 @@ static const char * const git_notes_usage[] = {
"git notes [--ref <notes_ref>] edit [<object>]",
"git notes [--ref <notes_ref>] show [<object>]",
"git notes [--ref <notes_ref>] remove [<object>]",
- "git notes [--ref <notes_ref>] prune",
+ "git notes [--ref <notes_ref>] prune [-n | -v]",
NULL
};
@@ -67,7 +67,7 @@ static const char * const git_notes_remove_usage[] = {
};
static const char * const git_notes_prune_usage[] = {
- "git notes prune",
+ "git notes prune [<options>]",
NULL
};
@@ -313,7 +313,7 @@ int commit_notes(struct notes_tree *t, const char *msg)
return 0;
}
-combine_notes_fn *parse_combine_notes_fn(const char *v)
+combine_notes_fn parse_combine_notes_fn(const char *v)
{
if (!strcasecmp(v, "overwrite"))
return combine_notes_overwrite;
@@ -416,7 +416,7 @@ int notes_copy_from_stdin(int force, const char *rewrite_cmd)
{
struct strbuf buf = STRBUF_INIT;
struct notes_rewrite_cfg *c = NULL;
- struct notes_tree *t;
+ struct notes_tree *t = NULL;
int ret = 0;
if (rewrite_cmd) {
@@ -614,6 +614,10 @@ static int copy(int argc, const char **argv, const char *prefix)
}
}
+ if (argc < 2) {
+ error("too few parameters");
+ usage_with_options(git_notes_copy_usage, options);
+ }
if (2 < argc) {
error("too many parameters");
usage_with_options(git_notes_copy_usage, options);
@@ -792,7 +796,11 @@ static int remove_cmd(int argc, const char **argv, const char *prefix)
static int prune(int argc, const char **argv, const char *prefix)
{
struct notes_tree *t;
+ int show_only = 0, verbose = 0;
struct option options[] = {
+ OPT_BOOLEAN('n', "dry-run", &show_only,
+ "do not remove, show only"),
+ OPT_BOOLEAN('v', "verbose", &verbose, "report pruned notes"),
OPT_END()
};
@@ -806,8 +814,10 @@ static int prune(int argc, const char **argv, const char *prefix)
t = init_notes_check("prune");
- prune_notes(t);
- commit_notes(t, "Notes removed by 'git notes prune'");
+ prune_notes(t, (verbose ? NOTES_PRUNE_VERBOSE : 0) |
+ (show_only ? NOTES_PRUNE_VERBOSE|NOTES_PRUNE_DRYRUN : 0) );
+ if (!show_only)
+ commit_notes(t, "Notes removed by 'git notes prune'");
free_notes(t);
return 0;
}
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 97802585ea..0e81673118 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -18,8 +18,8 @@
#include "refs.h"
#ifndef NO_PTHREADS
-#include "thread-utils.h"
#include <pthread.h>
+#include "thread-utils.h"
#endif
static const char pack_usage[] =
@@ -1522,6 +1522,15 @@ static void find_deltas(struct object_entry **list, unsigned *list_size,
#ifndef NO_PTHREADS
+static void try_to_free_from_threads(size_t size)
+{
+ read_lock();
+ release_pack_memory(size, -1);
+ read_unlock();
+}
+
+try_to_free_t old_try_to_free_routine;
+
/*
* The main thread waits on the condition that (at least) one of the workers
* has stopped working (which is indicated in the .working member of
@@ -1552,14 +1561,16 @@ static pthread_cond_t progress_cond;
*/
static void init_threaded_search(void)
{
- pthread_mutex_init(&read_mutex, NULL);
+ init_recursive_mutex(&read_mutex);
pthread_mutex_init(&cache_mutex, NULL);
pthread_mutex_init(&progress_mutex, NULL);
pthread_cond_init(&progress_cond, NULL);
+ old_try_to_free_routine = set_try_to_free_routine(try_to_free_from_threads);
}
static void cleanup_threaded_search(void)
{
+ set_try_to_free_routine(old_try_to_free_routine);
pthread_cond_destroy(&progress_cond);
pthread_mutex_destroy(&read_mutex);
pthread_mutex_destroy(&cache_mutex);
diff --git a/builtin/patch-id.c b/builtin/patch-id.c
index af0911e4bd..512530022e 100644
--- a/builtin/patch-id.c
+++ b/builtin/patch-id.c
@@ -28,16 +28,42 @@ static int remove_space(char *line)
return dst - line;
}
-static void generate_id_list(void)
+static int scan_hunk_header(const char *p, int *p_before, int *p_after)
+{
+ static const char digits[] = "0123456789";
+ const char *q, *r;
+ int n;
+
+ q = p + 4;
+ n = strspn(q, digits);
+ if (q[n] == ',') {
+ q += n + 1;
+ n = strspn(q, digits);
+ }
+ if (n == 0 || q[n] != ' ' || q[n+1] != '+')
+ return 0;
+
+ r = q + n + 2;
+ n = strspn(r, digits);
+ if (r[n] == ',') {
+ r += n + 1;
+ n = strspn(r, digits);
+ }
+ if (n == 0)
+ return 0;
+
+ *p_before = atoi(q);
+ *p_after = atoi(r);
+ return 1;
+}
+
+int get_one_patchid(unsigned char *next_sha1, git_SHA_CTX *ctx)
{
- static unsigned char sha1[20];
static char line[1000];
- git_SHA_CTX ctx;
- int patchlen = 0;
+ int patchlen = 0, found_next = 0;
+ int before = -1, after = -1;
- git_SHA1_Init(&ctx);
while (fgets(line, sizeof(line), stdin) != NULL) {
- unsigned char n[20];
char *p = line;
int len;
@@ -45,32 +71,75 @@ static void generate_id_list(void)
p += 10;
else if (!memcmp(line, "commit ", 7))
p += 7;
+ else if (!memcmp(line, "From ", 5))
+ p += 5;
- if (!get_sha1_hex(p, n)) {
- flush_current_id(patchlen, sha1, &ctx);
- hashcpy(sha1, n);
- patchlen = 0;
- continue;
+ if (!get_sha1_hex(p, next_sha1)) {
+ found_next = 1;
+ break;
}
/* Ignore commit comments */
if (!patchlen && memcmp(line, "diff ", 5))
continue;
- /* Ignore git-diff index header */
- if (!memcmp(line, "index ", 6))
- continue;
+ /* Parsing diff header? */
+ if (before == -1) {
+ if (!memcmp(line, "index ", 6))
+ continue;
+ else if (!memcmp(line, "--- ", 4))
+ before = after = 1;
+ else if (!isalpha(line[0]))
+ break;
+ }
- /* Ignore line numbers when computing the SHA1 of the patch */
- if (!memcmp(line, "@@ -", 4))
- continue;
+ /* Looking for a valid hunk header? */
+ if (before == 0 && after == 0) {
+ if (!memcmp(line, "@@ -", 4)) {
+ /* Parse next hunk, but ignore line numbers. */
+ scan_hunk_header(line, &before, &after);
+ continue;
+ }
+
+ /* Split at the end of the patch. */
+ if (memcmp(line, "diff ", 5))
+ break;
+
+ /* Else we're parsing another header. */
+ before = after = -1;
+ }
+
+ /* If we get here, we're inside a hunk. */
+ if (line[0] == '-' || line[0] == ' ')
+ before--;
+ if (line[0] == '+' || line[0] == ' ')
+ after--;
/* Compute the sha without whitespace */
len = remove_space(line);
patchlen += len;
- git_SHA1_Update(&ctx, line, len);
+ git_SHA1_Update(ctx, line, len);
+ }
+
+ if (!found_next)
+ hashclr(next_sha1);
+
+ return patchlen;
+}
+
+static void generate_id_list(void)
+{
+ unsigned char sha1[20], n[20];
+ git_SHA_CTX ctx;
+ int patchlen;
+
+ git_SHA1_Init(&ctx);
+ hashclr(sha1);
+ while (!feof(stdin)) {
+ patchlen = get_one_patchid(n, &ctx);
+ flush_current_id(patchlen, sha1, &ctx);
+ hashcpy(sha1, n);
}
- flush_current_id(patchlen, sha1, &ctx);
}
static const char patch_id_usage[] = "git patch-id < patch";
diff --git a/builtin/prune.c b/builtin/prune.c
index 81f915ec31..99218ba49e 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -125,10 +125,9 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
const struct option options[] = {
- OPT_BOOLEAN('n', NULL, &show_only,
+ OPT_BOOLEAN('n', "dry-run", &show_only,
"do not remove, show only"),
- OPT_BOOLEAN('v', NULL, &verbose,
- "report pruned objects"),
+ OPT_BOOLEAN('v', "verbose", &verbose, "report pruned objects"),
OPT_DATE(0, "expire", &expire,
"expire objects older than <time>"),
OPT_END()
diff --git a/builtin/push.c b/builtin/push.c
index f4358b9d23..e655eb7695 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -22,13 +22,13 @@ static int progress;
static const char **refspec;
static int refspec_nr;
+static int refspec_alloc;
static void add_refspec(const char *ref)
{
- int nr = refspec_nr + 1;
- refspec = xrealloc(refspec, nr * sizeof(char *));
- refspec[nr-1] = ref;
- refspec_nr = nr;
+ refspec_nr++;
+ ALLOC_GROW(refspec, refspec_nr, refspec_alloc);
+ refspec[refspec_nr-1] = ref;
}
static void set_refspecs(const char **refs, int nr)
@@ -130,8 +130,8 @@ static int push_with_options(struct transport *transport, int flags)
if (nonfastforward && advice_push_nonfastforward) {
fprintf(stderr, "To prevent you from losing history, non-fast-forward updates were rejected\n"
- "Merge the remote changes before pushing again. See the 'Note about\n"
- "fast-forwards' section of 'git push --help' for details.\n");
+ "Merge the remote changes (e.g. 'git pull') before pushing again. See the\n"
+ "'Note about fast-forwards' section of 'git push --help' for details.\n");
}
return 1;
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index 8bdcab1138..9ad1e66916 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -219,14 +219,9 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
* "-m ent" or "--reset ent" form), we can obtain a fully
* valid cache-tree because the index must match exactly
* what came from the tree.
- *
- * The same holds true if we are switching between two trees
- * using read-tree -m A B. The index must match B after that.
*/
if (nr_trees == 1 && !opts.prefix)
prime_cache_tree(&active_cache_tree, trees[0]);
- else if (nr_trees == 2 && opts.merge)
- prime_cache_tree(&active_cache_tree, trees[1]);
if (write_cache(newfd, active_cache, active_nr) ||
commit_locked_index(&lock_file))
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 0559fcc871..760817dbd7 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -9,6 +9,7 @@
#include "object.h"
#include "remote.h"
#include "transport.h"
+#include "string-list.h"
static const char receive_pack_usage[] = "git receive-pack <git-dir>";
@@ -16,7 +17,7 @@ enum deny_action {
DENY_UNCONFIGURED,
DENY_IGNORE,
DENY_WARN,
- DENY_REFUSE,
+ DENY_REFUSE
};
static int deny_deletes;
@@ -129,13 +130,12 @@ static void write_head_info(void)
struct command {
struct command *next;
const char *error_string;
+ unsigned int skip_update;
unsigned char old_sha1[20];
unsigned char new_sha1[20];
char ref_name[FLEX_ARRAY]; /* more */
};
-static struct command *commands;
-
static const char pre_receive_hook[] = "hooks/pre-receive";
static const char post_receive_hook[] = "hooks/post-receive";
@@ -188,7 +188,7 @@ static int copy_to_sideband(int in, int out, void *arg)
return 0;
}
-static int run_receive_hook(const char *hook_name)
+static int run_receive_hook(struct command *commands, const char *hook_name)
{
static char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4];
struct command *cmd;
@@ -447,15 +447,15 @@ static const char *update(struct command *cmd)
static char update_post_hook[] = "hooks/post-update";
-static void run_update_post_hook(struct command *cmd)
+static void run_update_post_hook(struct command *commands)
{
- struct command *cmd_p;
+ struct command *cmd;
int argc;
const char **argv;
struct child_process proc;
- for (argc = 0, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) {
- if (cmd_p->error_string)
+ for (argc = 0, cmd = commands; cmd; cmd = cmd->next) {
+ if (cmd->error_string)
continue;
argc++;
}
@@ -464,12 +464,12 @@ static void run_update_post_hook(struct command *cmd)
argv = xmalloc(sizeof(*argv) * (2 + argc));
argv[0] = update_post_hook;
- for (argc = 1, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) {
+ for (argc = 1, cmd = commands; cmd; cmd = cmd->next) {
char *p;
- if (cmd_p->error_string)
+ if (cmd->error_string)
continue;
- p = xmalloc(strlen(cmd_p->ref_name) + 1);
- strcpy(p, cmd_p->ref_name);
+ p = xmalloc(strlen(cmd->ref_name) + 1);
+ strcpy(p, cmd->ref_name);
argv[argc] = p;
argc++;
}
@@ -488,37 +488,92 @@ static void run_update_post_hook(struct command *cmd)
}
}
-static void execute_commands(const char *unpacker_error)
+static void check_aliased_update(struct command *cmd, struct string_list *list)
+{
+ struct string_list_item *item;
+ struct command *dst_cmd;
+ unsigned char sha1[20];
+ char cmd_oldh[41], cmd_newh[41], dst_oldh[41], dst_newh[41];
+ int flag;
+
+ const char *dst_name = resolve_ref(cmd->ref_name, sha1, 0, &flag);
+
+ if (!(flag & REF_ISSYMREF))
+ return;
+
+ if ((item = string_list_lookup(list, dst_name)) == NULL)
+ return;
+
+ cmd->skip_update = 1;
+
+ dst_cmd = (struct command *) item->util;
+
+ if (!hashcmp(cmd->old_sha1, dst_cmd->old_sha1) &&
+ !hashcmp(cmd->new_sha1, dst_cmd->new_sha1))
+ return;
+
+ dst_cmd->skip_update = 1;
+
+ strcpy(cmd_oldh, find_unique_abbrev(cmd->old_sha1, DEFAULT_ABBREV));
+ strcpy(cmd_newh, find_unique_abbrev(cmd->new_sha1, DEFAULT_ABBREV));
+ strcpy(dst_oldh, find_unique_abbrev(dst_cmd->old_sha1, DEFAULT_ABBREV));
+ strcpy(dst_newh, find_unique_abbrev(dst_cmd->new_sha1, DEFAULT_ABBREV));
+ rp_error("refusing inconsistent update between symref '%s' (%s..%s) and"
+ " its target '%s' (%s..%s)",
+ cmd->ref_name, cmd_oldh, cmd_newh,
+ dst_cmd->ref_name, dst_oldh, dst_newh);
+
+ cmd->error_string = dst_cmd->error_string =
+ "inconsistent aliased update";
+}
+
+static void check_aliased_updates(struct command *commands)
+{
+ struct command *cmd;
+ struct string_list ref_list = STRING_LIST_INIT_NODUP;
+
+ for (cmd = commands; cmd; cmd = cmd->next) {
+ struct string_list_item *item =
+ string_list_append(&ref_list, cmd->ref_name);
+ item->util = (void *)cmd;
+ }
+ sort_string_list(&ref_list);
+
+ for (cmd = commands; cmd; cmd = cmd->next)
+ check_aliased_update(cmd, &ref_list);
+
+ string_list_clear(&ref_list, 0);
+}
+
+static void execute_commands(struct command *commands, const char *unpacker_error)
{
- struct command *cmd = commands;
+ struct command *cmd;
unsigned char sha1[20];
if (unpacker_error) {
- while (cmd) {
+ for (cmd = commands; cmd; cmd = cmd->next)
cmd->error_string = "n/a (unpacker error)";
- cmd = cmd->next;
- }
return;
}
- if (run_receive_hook(pre_receive_hook)) {
- while (cmd) {
+ if (run_receive_hook(commands, pre_receive_hook)) {
+ for (cmd = commands; cmd; cmd = cmd->next)
cmd->error_string = "pre-receive hook declined";
- cmd = cmd->next;
- }
return;
}
+ check_aliased_updates(commands);
+
head_name = resolve_ref("HEAD", sha1, 0, NULL);
- while (cmd) {
- cmd->error_string = update(cmd);
- cmd = cmd->next;
- }
+ for (cmd = commands; cmd; cmd = cmd->next)
+ if (!cmd->skip_update)
+ cmd->error_string = update(cmd);
}
-static void read_head_info(void)
+static struct command *read_head_info(void)
{
+ struct command *commands = NULL;
struct command **p = &commands;
for (;;) {
static char line[1000];
@@ -548,15 +603,14 @@ static void read_head_info(void)
if (strstr(refname + reflen + 1, "side-band-64k"))
use_sideband = LARGE_PACKET_MAX;
}
- cmd = xmalloc(sizeof(struct command) + len - 80);
+ cmd = xcalloc(1, sizeof(struct command) + len - 80);
hashcpy(cmd->old_sha1, old_sha1);
hashcpy(cmd->new_sha1, new_sha1);
memcpy(cmd->ref_name, line + 82, len - 81);
- cmd->error_string = NULL;
- cmd->next = NULL;
*p = cmd;
p = &cmd->next;
}
+ return commands;
}
static const char *parse_pack_header(struct pack_header *hdr)
@@ -643,7 +697,7 @@ static const char *unpack(void)
}
}
-static void report(const char *unpack_status)
+static void report(struct command *commands, const char *unpack_status)
{
struct command *cmd;
struct strbuf buf = STRBUF_INIT;
@@ -667,12 +721,12 @@ static void report(const char *unpack_status)
strbuf_release(&buf);
}
-static int delete_only(struct command *cmd)
+static int delete_only(struct command *commands)
{
- while (cmd) {
+ struct command *cmd;
+ for (cmd = commands; cmd; cmd = cmd->next) {
if (!is_null_sha1(cmd->new_sha1))
return 0;
- cmd = cmd->next;
}
return 1;
}
@@ -722,6 +776,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
int stateless_rpc = 0;
int i;
char *dir = NULL;
+ struct command *commands;
argv++;
for (i = 1; i < argc; i++) {
@@ -772,18 +827,17 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
if (advertise_refs)
return 0;
- read_head_info();
- if (commands) {
+ if ((commands = read_head_info()) != NULL) {
const char *unpack_status = NULL;
if (!delete_only(commands))
unpack_status = unpack();
- execute_commands(unpack_status);
+ execute_commands(commands, unpack_status);
if (pack_lockfile)
unlink_or_warn(pack_lockfile);
if (report_status)
- report(unpack_status);
- run_receive_hook(post_receive_hook);
+ report(commands, unpack_status);
+ run_receive_hook(commands, post_receive_hook);
run_update_post_hook(commands);
if (auto_gc) {
const char *argv_gc_auto[] = {
diff --git a/builtin/reflog.c b/builtin/reflog.c
index bd7880dc04..ebf610e64a 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -34,8 +34,13 @@ struct cmd_reflog_expire_cb {
struct expire_reflog_cb {
FILE *newlog;
- const char *ref;
- struct commit *ref_commit;
+ enum {
+ UE_NORMAL,
+ UE_ALWAYS,
+ UE_HEAD
+ } unreachable_expire_kind;
+ struct commit_list *mark_list;
+ unsigned long mark_limit;
struct cmd_reflog_expire_cb *cmd;
unsigned char last_kept_sha1[20];
};
@@ -210,46 +215,23 @@ static int keep_entry(struct commit **it, unsigned char *sha1)
return 1;
}
-static int unreachable(struct expire_reflog_cb *cb, struct commit *commit, unsigned char *sha1)
+/*
+ * Starting from commits in the cb->mark_list, mark commits that are
+ * reachable from them. Stop the traversal at commits older than
+ * the expire_limit and queue them back, so that the caller can call
+ * us again to restart the traversal with longer expire_limit.
+ */
+static void mark_reachable(struct expire_reflog_cb *cb)
{
- /*
- * We may or may not have the commit yet - if not, look it
- * up using the supplied sha1.
- */
- if (!commit) {
- if (is_null_sha1(sha1))
- return 0;
-
- commit = lookup_commit_reference_gently(sha1, 1);
-
- /* Not a commit -- keep it */
- if (!commit)
- return 0;
- }
-
- /* Reachable from the current ref? Don't prune. */
- if (commit->object.flags & REACHABLE)
- return 0;
- if (in_merge_bases(commit, &cb->ref_commit, 1))
- return 0;
-
- /* We can't reach it - prune it. */
- return 1;
-}
+ struct commit *commit;
+ struct commit_list *pending;
+ unsigned long expire_limit = cb->mark_limit;
+ struct commit_list *leftover = NULL;
-static void mark_reachable(struct commit *commit, unsigned long expire_limit)
-{
- /*
- * We need to compute whether the commit on either side of a reflog
- * entry is reachable from the tip of the ref for all entries.
- * Mark commits that are reachable from the tip down to the
- * time threshold first; we know a commit marked thusly is
- * reachable from the tip without running in_merge_bases()
- * at all.
- */
- struct commit_list *pending = NULL;
+ for (pending = cb->mark_list; pending; pending = pending->next)
+ pending->item->object.flags &= ~REACHABLE;
- commit_list_insert(commit, &pending);
+ pending = cb->mark_list;
while (pending) {
struct commit_list *entry = pending;
struct commit_list *parent;
@@ -261,8 +243,11 @@ static void mark_reachable(struct commit *commit, unsigned long expire_limit)
if (parse_commit(commit))
continue;
commit->object.flags |= REACHABLE;
- if (commit->date < expire_limit)
+ if (commit->date < expire_limit) {
+ commit_list_insert(commit, &leftover);
continue;
+ }
+ commit->object.flags |= REACHABLE;
parent = commit->parents;
while (parent) {
commit = parent->item;
@@ -272,6 +257,36 @@ static void mark_reachable(struct commit *commit, unsigned long expire_limit)
commit_list_insert(commit, &pending);
}
}
+ cb->mark_list = leftover;
+}
+
+static int unreachable(struct expire_reflog_cb *cb, struct commit *commit, unsigned char *sha1)
+{
+ /*
+ * We may or may not have the commit yet - if not, look it
+ * up using the supplied sha1.
+ */
+ if (!commit) {
+ if (is_null_sha1(sha1))
+ return 0;
+
+ commit = lookup_commit_reference_gently(sha1, 1);
+
+ /* Not a commit -- keep it */
+ if (!commit)
+ return 0;
+ }
+
+ /* Reachable from the current ref? Don't prune. */
+ if (commit->object.flags & REACHABLE)
+ return 0;
+
+ if (cb->mark_list && cb->mark_limit) {
+ cb->mark_limit = 0; /* dig down to the root */
+ mark_reachable(cb);
+ }
+
+ return !(commit->object.flags & REACHABLE);
}
static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
@@ -293,7 +308,7 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
goto prune;
if (timestamp < cb->cmd->expire_unreachable) {
- if (!cb->ref_commit)
+ if (cb->unreachable_expire_kind == UE_ALWAYS)
goto prune;
if (unreachable(cb, old, osha1) || unreachable(cb, new, nsha1))
goto prune;
@@ -320,12 +335,27 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
return 0;
}
+static int push_tip_to_list(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
+{
+ struct commit_list **list = cb_data;
+ struct commit *tip_commit;
+ if (flags & REF_ISSYMREF)
+ return 0;
+ tip_commit = lookup_commit_reference_gently(sha1, 1);
+ if (!tip_commit)
+ return 0;
+ commit_list_insert(tip_commit, list);
+ return 0;
+}
+
static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data)
{
struct cmd_reflog_expire_cb *cmd = cb_data;
struct expire_reflog_cb cb;
struct ref_lock *lock;
char *log_file, *newlog_path = NULL;
+ struct commit *tip_commit;
+ struct commit_list *tips;
int status = 0;
memset(&cb, 0, sizeof(cb));
@@ -345,14 +375,49 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
cb.newlog = fopen(newlog_path, "w");
}
- cb.ref_commit = lookup_commit_reference_gently(sha1, 1);
- cb.ref = ref;
cb.cmd = cmd;
- if (cb.ref_commit)
- mark_reachable(cb.ref_commit, cmd->expire_total);
+
+ if (!cmd->expire_unreachable || !strcmp(ref, "HEAD")) {
+ tip_commit = NULL;
+ cb.unreachable_expire_kind = UE_HEAD;
+ } else {
+ tip_commit = lookup_commit_reference_gently(sha1, 1);
+ if (!tip_commit)
+ cb.unreachable_expire_kind = UE_ALWAYS;
+ else
+ cb.unreachable_expire_kind = UE_NORMAL;
+ }
+
+ if (cmd->expire_unreachable <= cmd->expire_total)
+ cb.unreachable_expire_kind = UE_ALWAYS;
+
+ cb.mark_list = NULL;
+ tips = NULL;
+ if (cb.unreachable_expire_kind != UE_ALWAYS) {
+ if (cb.unreachable_expire_kind == UE_HEAD) {
+ struct commit_list *elem;
+ for_each_ref(push_tip_to_list, &tips);
+ for (elem = tips; elem; elem = elem->next)
+ commit_list_insert(elem->item, &cb.mark_list);
+ } else {
+ commit_list_insert(tip_commit, &cb.mark_list);
+ }
+ cb.mark_limit = cmd->expire_total;
+ mark_reachable(&cb);
+ }
+
for_each_reflog_ent(ref, expire_reflog_ent, &cb);
- if (cb.ref_commit)
- clear_commit_marks(cb.ref_commit, REACHABLE);
+
+ if (cb.unreachable_expire_kind != UE_ALWAYS) {
+ if (cb.unreachable_expire_kind == UE_HEAD) {
+ struct commit_list *elem;
+ for (elem = tips; elem; elem = elem->next)
+ clear_commit_marks(tip_commit, REACHABLE);
+ free_commit_list(tips);
+ } else {
+ clear_commit_marks(tip_commit, REACHABLE);
+ }
+ }
finish:
if (cb.newlog) {
if (fclose(cb.newlog)) {
diff --git a/builtin/remote.c b/builtin/remote.c
index 277765b864..48e0a6bf26 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -16,6 +16,7 @@ static const char * const builtin_remote_usage[] = {
"git remote [-v | --verbose] show [-n] <name>",
"git remote prune [-n | --dry-run] <name>",
"git remote [-v | --verbose] update [-p | --prune] [group | remote]",
+ "git remote set-branches <name> [--add] <branch>...",
"git remote set-url <name> <newurl> [<oldurl>]",
"git remote set-url --add <name> <newurl>",
"git remote set-url --delete <name> <url>",
@@ -42,6 +43,12 @@ static const char * const builtin_remote_sethead_usage[] = {
NULL
};
+static const char * const builtin_remote_setbranches_usage[] = {
+ "git remote set-branches <name> <branch>...",
+ "git remote set-branches --add <name> <branch>...",
+ NULL
+};
+
static const char * const builtin_remote_show_usage[] = {
"git remote show [<options>] <name>",
NULL
@@ -87,7 +94,7 @@ static int opt_parse_track(const struct option *opt, const char *arg, int not)
if (not)
string_list_clear(list, 0);
else
- string_list_append(arg, list);
+ string_list_append(list, arg);
return 0;
}
@@ -104,10 +111,30 @@ static int fetch_remote(const char *name)
return 0;
}
+enum {
+ TAGS_UNSET = 0,
+ TAGS_DEFAULT = 1,
+ TAGS_SET = 2
+};
+
+static int add_branch(const char *key, const char *branchname,
+ const char *remotename, int mirror, struct strbuf *tmp)
+{
+ strbuf_reset(tmp);
+ strbuf_addch(tmp, '+');
+ if (mirror)
+ strbuf_addf(tmp, "refs/%s:refs/%s",
+ branchname, branchname);
+ else
+ strbuf_addf(tmp, "refs/heads/%s:refs/remotes/%s/%s",
+ branchname, remotename, branchname);
+ return git_config_set_multivar(key, tmp->buf, "^$", 0);
+}
+
static int add(int argc, const char **argv)
{
- int fetch = 0, mirror = 0;
- struct string_list track = { NULL, 0, 0 };
+ int fetch = 0, mirror = 0, fetch_tags = TAGS_DEFAULT;
+ struct string_list track = STRING_LIST_INIT_NODUP;
const char *master = NULL;
struct remote *remote;
struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;
@@ -116,6 +143,11 @@ static int add(int argc, const char **argv)
struct option options[] = {
OPT_BOOLEAN('f', "fetch", &fetch, "fetch the remote branches"),
+ OPT_SET_INT(0, "tags", &fetch_tags,
+ "import all tags and associated objects when fetching",
+ TAGS_SET),
+ OPT_SET_INT(0, NULL, &fetch_tags,
+ "or do not fetch any tag at all (--no-tags)", TAGS_UNSET),
OPT_CALLBACK('t', "track", &track, "branch",
"branch(es) to track", opt_parse_track),
OPT_STRING('m', "master", &master, "branch", "master branch"),
@@ -149,19 +181,10 @@ static int add(int argc, const char **argv)
strbuf_addf(&buf, "remote.%s.fetch", name);
if (track.nr == 0)
- string_list_append("*", &track);
+ string_list_append(&track, "*");
for (i = 0; i < track.nr; i++) {
- struct string_list_item *item = track.items + i;
-
- strbuf_reset(&buf2);
- strbuf_addch(&buf2, '+');
- if (mirror)
- strbuf_addf(&buf2, "refs/%s:refs/%s",
- item->string, item->string);
- else
- strbuf_addf(&buf2, "refs/heads/%s:refs/remotes/%s/%s",
- item->string, name, item->string);
- if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0))
+ if (add_branch(buf.buf, track.items[i].string,
+ name, mirror, &buf2))
return 1;
}
@@ -172,6 +195,14 @@ static int add(int argc, const char **argv)
return 1;
}
+ if (fetch_tags != TAGS_DEFAULT) {
+ strbuf_reset(&buf);
+ strbuf_addf(&buf, "remote.%s.tagopt", name);
+ if (git_config_set(buf.buf,
+ fetch_tags == TAGS_SET ? "--tags" : "--no-tags"))
+ return 1;
+ }
+
if (fetch && fetch_remote(name))
return 1;
@@ -232,7 +263,7 @@ static int config_read_branches(const char *key, const char *value, void *cb)
} else
return 0;
- item = string_list_insert(name, &branch_list);
+ item = string_list_insert(&branch_list, name);
if (!item->util)
item->util = xcalloc(sizeof(struct branch_info), 1);
@@ -247,11 +278,11 @@ static int config_read_branches(const char *key, const char *value, void *cb)
while (space) {
char *merge;
merge = xstrndup(value, space - value);
- string_list_append(merge, &info->merge);
+ string_list_append(&info->merge, merge);
value = abbrev_branch(space + 1);
space = strchr(value, ' ');
}
- string_list_append(xstrdup(value), &info->merge);
+ string_list_append(&info->merge, xstrdup(value));
} else
info->rebase = git_config_bool(orig_key, value);
}
@@ -288,14 +319,14 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
for (ref = fetch_map; ref; ref = ref->next) {
unsigned char sha1[20];
if (!ref->peer_ref || read_ref(ref->peer_ref->name, sha1))
- string_list_append(abbrev_branch(ref->name), &states->new);
+ string_list_append(&states->new, abbrev_branch(ref->name));
else
- string_list_append(abbrev_branch(ref->name), &states->tracked);
+ string_list_append(&states->tracked, abbrev_branch(ref->name));
}
stale_refs = get_stale_heads(states->remote, fetch_map);
for (ref = stale_refs; ref; ref = ref->next) {
struct string_list_item *item =
- string_list_append(abbrev_branch(ref->name), &states->stale);
+ string_list_append(&states->stale, abbrev_branch(ref->name));
item->util = xstrdup(ref->name);
}
free_refs(stale_refs);
@@ -317,7 +348,7 @@ struct push_info {
PUSH_STATUS_UPTODATE,
PUSH_STATUS_FASTFORWARD,
PUSH_STATUS_OUTOFDATE,
- PUSH_STATUS_NOTQUERIED,
+ PUSH_STATUS_NOTQUERIED
} status;
};
@@ -344,8 +375,8 @@ static int get_push_ref_states(const struct ref *remote_refs,
continue;
hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
- item = string_list_append(abbrev_branch(ref->peer_ref->name),
- &states->push);
+ item = string_list_append(&states->push,
+ abbrev_branch(ref->peer_ref->name));
item->util = xcalloc(sizeof(struct push_info), 1);
info = item->util;
info->forced = ref->force;
@@ -380,7 +411,7 @@ static int get_push_ref_states_noquery(struct ref_states *states)
states->push.strdup_strings = 1;
if (!remote->push_refspec_nr) {
- item = string_list_append("(matching)", &states->push);
+ item = string_list_append(&states->push, "(matching)");
info = item->util = xcalloc(sizeof(struct push_info), 1);
info->status = PUSH_STATUS_NOTQUERIED;
info->dest = xstrdup(item->string);
@@ -388,11 +419,11 @@ static int get_push_ref_states_noquery(struct ref_states *states)
for (i = 0; i < remote->push_refspec_nr; i++) {
struct refspec *spec = remote->push + i;
if (spec->matching)
- item = string_list_append("(matching)", &states->push);
+ item = string_list_append(&states->push, "(matching)");
else if (strlen(spec->src))
- item = string_list_append(spec->src, &states->push);
+ item = string_list_append(&states->push, spec->src);
else
- item = string_list_append("(delete)", &states->push);
+ item = string_list_append(&states->push, "(delete)");
info = item->util = xcalloc(sizeof(struct push_info), 1);
info->forced = spec->force;
@@ -416,7 +447,7 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
fetch_map, 1);
for (ref = matches; ref; ref = ref->next)
- string_list_append(abbrev_branch(ref->name), &states->heads);
+ string_list_append(&states->heads, abbrev_branch(ref->name));
free_refs(fetch_map);
free_refs(matches);
@@ -480,8 +511,8 @@ static int add_branch_for_removal(const char *refname,
if (prefixcmp(refname, "refs/remotes")) {
/* advise user how to delete local branches */
if (!prefixcmp(refname, "refs/heads/"))
- string_list_append(abbrev_branch(refname),
- branches->skipped);
+ string_list_append(branches->skipped,
+ abbrev_branch(refname));
/* silently skip over other non-remote refs */
return 0;
}
@@ -490,7 +521,7 @@ static int add_branch_for_removal(const char *refname,
if (flags & REF_ISSYMREF)
return unlink(git_path("%s", refname));
- item = string_list_append(refname, branches->branches);
+ item = string_list_append(branches->branches, refname);
item->util = xmalloc(20);
hashcpy(item->util, sha1);
@@ -515,7 +546,7 @@ static int read_remote_branches(const char *refname,
strbuf_addf(&buf, "refs/remotes/%s", rename->old);
if (!prefixcmp(refname, buf.buf)) {
- item = string_list_append(xstrdup(refname), rename->remote_branches);
+ item = string_list_append(rename->remote_branches, xstrdup(refname));
symref = resolve_ref(refname, orig_sha1, 1, &flag);
if (flag & REF_ISSYMREF)
item->util = xstrdup(symref);
@@ -565,7 +596,7 @@ static int mv(int argc, const char **argv)
};
struct remote *oldremote, *newremote;
struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT, buf3 = STRBUF_INIT;
- struct string_list remote_branches = { NULL, 0, 0, 0 };
+ struct string_list remote_branches = STRING_LIST_INIT_NODUP;
struct rename_info rename;
int i;
@@ -703,13 +734,16 @@ static int rm(int argc, const char **argv)
struct remote *remote;
struct strbuf buf = STRBUF_INIT;
struct known_remotes known_remotes = { NULL, NULL };
- struct string_list branches = { NULL, 0, 0, 1 };
- struct string_list skipped = { NULL, 0, 0, 1 };
- struct branches_for_remote cb_data = {
- NULL, &branches, &skipped, &known_remotes
- };
+ struct string_list branches = STRING_LIST_INIT_DUP;
+ struct string_list skipped = STRING_LIST_INIT_DUP;
+ struct branches_for_remote cb_data;
int i, result;
+ memset(&cb_data, 0, sizeof(cb_data));
+ cb_data.branches = &branches;
+ cb_data.skipped = &skipped;
+ cb_data.keep = &known_remotes;
+
if (argc != 2)
usage_with_options(builtin_remote_rm_usage, options);
@@ -798,7 +832,7 @@ static int append_ref_to_tracked_list(const char *refname,
memset(&refspec, 0, sizeof(refspec));
refspec.dst = (char *)refname;
if (!remote_find_tracking(states->remote, &refspec))
- string_list_append(abbrev_branch(refspec.src), &states->tracked);
+ string_list_append(&states->tracked, abbrev_branch(refspec.src));
return 0;
}
@@ -851,7 +885,7 @@ static int add_remote_to_show_info(struct string_list_item *item, void *cb_data)
int n = strlen(item->string);
if (n > info->width)
info->width = n;
- string_list_insert(item->string, info->list);
+ string_list_insert(info->list, item->string);
return 0;
}
@@ -898,7 +932,7 @@ static int add_local_to_show_info(struct string_list_item *branch_item, void *cb
if (branch_info->rebase)
show_info->any_rebase = 1;
- item = string_list_insert(branch_item->string, show_info->list);
+ item = string_list_insert(show_info->list, branch_item->string);
item->util = branch_info;
return 0;
@@ -946,7 +980,7 @@ static int add_push_to_show_info(struct string_list_item *push_item, void *cb_da
show_info->width = n;
if ((n = strlen(push_info->dest)) > show_info->width2)
show_info->width2 = n;
- item = string_list_append(push_item->string, show_info->list);
+ item = string_list_append(show_info->list, push_item->string);
item->util = push_item->util;
return 0;
}
@@ -1010,7 +1044,7 @@ static int show(int argc, const char **argv)
OPT_END()
};
struct ref_states states;
- struct string_list info_list = { NULL, 0, 0, 0 };
+ struct string_list info_list = STRING_LIST_INIT_NODUP;
struct show_info info;
argc = parse_options(argc, argv, NULL, options, builtin_remote_show_usage,
@@ -1062,24 +1096,24 @@ static int show(int argc, const char **argv)
/* remote branch info */
info.width = 0;
- for_each_string_list(add_remote_to_show_info, &states.new, &info);
- for_each_string_list(add_remote_to_show_info, &states.tracked, &info);
- for_each_string_list(add_remote_to_show_info, &states.stale, &info);
+ for_each_string_list(&states.new, add_remote_to_show_info, &info);
+ for_each_string_list(&states.tracked, add_remote_to_show_info, &info);
+ for_each_string_list(&states.stale, add_remote_to_show_info, &info);
if (info.list->nr)
printf(" Remote branch%s:%s\n",
info.list->nr > 1 ? "es" : "",
no_query ? " (status not queried)" : "");
- for_each_string_list(show_remote_info_item, info.list, &info);
+ for_each_string_list(info.list, show_remote_info_item, &info);
string_list_clear(info.list, 0);
/* git pull info */
info.width = 0;
info.any_rebase = 0;
- for_each_string_list(add_local_to_show_info, &branch_list, &info);
+ for_each_string_list(&branch_list, add_local_to_show_info, &info);
if (info.list->nr)
printf(" Local branch%s configured for 'git pull':\n",
info.list->nr > 1 ? "es" : "");
- for_each_string_list(show_local_info_item, info.list, &info);
+ for_each_string_list(info.list, show_local_info_item, &info);
string_list_clear(info.list, 0);
/* git push info */
@@ -1087,14 +1121,14 @@ static int show(int argc, const char **argv)
printf(" Local refs will be mirrored by 'git push'\n");
info.width = info.width2 = 0;
- for_each_string_list(add_push_to_show_info, &states.push, &info);
+ for_each_string_list(&states.push, add_push_to_show_info, &info);
qsort(info.list->items, info.list->nr,
sizeof(*info.list->items), cmp_string_with_push);
if (info.list->nr)
printf(" Local ref%s configured for 'git push'%s:\n",
info.list->nr > 1 ? "s" : "",
no_query ? " (status not queried)" : "");
- for_each_string_list(show_push_info_item, info.list, &info);
+ for_each_string_list(info.list, show_push_info_item, &info);
string_list_clear(info.list, 0);
free_remote_ref_states(&states);
@@ -1265,6 +1299,72 @@ static int update(int argc, const char **argv)
return run_command_v_opt(fetch_argv, RUN_GIT_CMD);
}
+static int remove_all_fetch_refspecs(const char *remote, const char *key)
+{
+ return git_config_set_multivar(key, NULL, NULL, 1);
+}
+
+static int add_branches(struct remote *remote, const char **branches,
+ const char *key)
+{
+ const char *remotename = remote->name;
+ int mirror = remote->mirror;
+ struct strbuf refspec = STRBUF_INIT;
+
+ for (; *branches; branches++)
+ if (add_branch(key, *branches, remotename, mirror, &refspec)) {
+ strbuf_release(&refspec);
+ return 1;
+ }
+
+ strbuf_release(&refspec);
+ return 0;
+}
+
+static int set_remote_branches(const char *remotename, const char **branches,
+ int add_mode)
+{
+ struct strbuf key = STRBUF_INIT;
+ struct remote *remote;
+
+ strbuf_addf(&key, "remote.%s.fetch", remotename);
+
+ if (!remote_is_configured(remotename))
+ die("No such remote '%s'", remotename);
+ remote = remote_get(remotename);
+
+ if (!add_mode && remove_all_fetch_refspecs(remotename, key.buf)) {
+ strbuf_release(&key);
+ return 1;
+ }
+ if (add_branches(remote, branches, key.buf)) {
+ strbuf_release(&key);
+ return 1;
+ }
+
+ strbuf_release(&key);
+ return 0;
+}
+
+static int set_branches(int argc, const char **argv)
+{
+ int add_mode = 0;
+ struct option options[] = {
+ OPT_BOOLEAN('\0', "add", &add_mode, "add branch"),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, NULL, options,
+ builtin_remote_setbranches_usage, 0);
+ if (argc == 0) {
+ error("no remote specified");
+ usage_with_options(builtin_remote_seturl_usage, options);
+ }
+ argv[argc] = NULL;
+
+ return set_remote_branches(argv[0], argv + 1, add_mode);
+}
+
static int set_url(int argc, const char **argv)
{
int i, push_mode = 0, add_mode = 0, delete_mode = 0;
@@ -1360,10 +1460,10 @@ static int get_one_entry(struct remote *remote, void *priv)
if (remote->url_nr > 0) {
strbuf_addf(&url_buf, "%s (fetch)", remote->url[0]);
- string_list_append(remote->name, list)->util =
+ string_list_append(list, remote->name)->util =
strbuf_detach(&url_buf, NULL);
} else
- string_list_append(remote->name, list)->util = NULL;
+ string_list_append(list, remote->name)->util = NULL;
if (remote->pushurl_nr) {
url = remote->pushurl;
url_nr = remote->pushurl_nr;
@@ -1374,7 +1474,7 @@ static int get_one_entry(struct remote *remote, void *priv)
for (i = 0; i < url_nr; i++)
{
strbuf_addf(&url_buf, "%s (push)", url[i]);
- string_list_append(remote->name, list)->util =
+ string_list_append(list, remote->name)->util =
strbuf_detach(&url_buf, NULL);
}
@@ -1383,7 +1483,7 @@ static int get_one_entry(struct remote *remote, void *priv)
static int show_all(void)
{
- struct string_list list = { NULL, 0, 0 };
+ struct string_list list = STRING_LIST_INIT_NODUP;
int result;
list.strdup_strings = 1;
@@ -1430,6 +1530,8 @@ int cmd_remote(int argc, const char **argv, const char *prefix)
result = rm(argc, argv);
else if (!strcmp(argv[0], "set-head"))
result = set_head(argc, argv);
+ else if (!strcmp(argv[0], "set-branches"))
+ result = set_branches(argc, argv);
else if (!strcmp(argv[0], "set-url"))
result = set_url(argc, argv);
else if (!strcmp(argv[0], "show"))
diff --git a/builtin/rerere.c b/builtin/rerere.c
index 34f9acee91..642bf35587 100644
--- a/builtin/rerere.c
+++ b/builtin/rerere.c
@@ -1,13 +1,16 @@
#include "builtin.h"
#include "cache.h"
#include "dir.h"
+#include "parse-options.h"
#include "string-list.h"
#include "rerere.h"
#include "xdiff/xdiff.h"
#include "xdiff-interface.h"
-static const char git_rerere_usage[] =
-"git rerere [clear | status | diff | gc]";
+static const char * const rerere_usage[] = {
+ "git rerere [clear | status | diff | gc]",
+ NULL,
+};
/* these values are days */
static int cutoff_noresolve = 15;
@@ -19,6 +22,12 @@ static time_t rerere_created_at(const char *name)
return stat(rerere_path(name, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
}
+static time_t rerere_last_used_at(const char *name)
+{
+ struct stat st;
+ return stat(rerere_path(name, "postimage"), &st) ? (time_t) 0 : st.st_mtime;
+}
+
static void unlink_rr_item(const char *name)
{
unlink(rerere_path(name, "thisimage"));
@@ -40,7 +49,7 @@ static int git_rerere_gc_config(const char *var, const char *value, void *cb)
static void garbage_collect(struct string_list *rr)
{
- struct string_list to_remove = { NULL, 0, 0, 1 };
+ struct string_list to_remove = STRING_LIST_INIT_DUP;
DIR *dir;
struct dirent *e;
int i, cutoff;
@@ -53,13 +62,18 @@ static void garbage_collect(struct string_list *rr)
while ((e = readdir(dir))) {
if (is_dot_or_dotdot(e->d_name))
continue;
- then = rerere_created_at(e->d_name);
- if (!then)
- continue;
- cutoff = (has_rerere_resolution(e->d_name)
- ? cutoff_resolve : cutoff_noresolve);
+
+ then = rerere_last_used_at(e->d_name);
+ if (then) {
+ cutoff = cutoff_resolve;
+ } else {
+ then = rerere_created_at(e->d_name);
+ if (!then)
+ continue;
+ cutoff = cutoff_noresolve;
+ }
if (then < now - cutoff * 86400)
- string_list_append(e->d_name, &to_remove);
+ string_list_append(&to_remove, e->d_name);
}
for (i = 0; i < to_remove.nr; i++)
unlink_rr_item(to_remove.items[i].string);
@@ -89,7 +103,7 @@ static int diff_two(const char *file1, const char *label1,
printf("--- a/%s\n+++ b/%s\n", label1, label2);
fflush(stdout);
memset(&xpp, 0, sizeof(xpp));
- xpp.flags = XDF_NEED_MINIMAL;
+ xpp.flags = 0;
memset(&xecfg, 0, sizeof(xecfg));
xecfg.ctxlen = 3;
ecb.outf = outf;
@@ -102,26 +116,27 @@ static int diff_two(const char *file1, const char *label1,
int cmd_rerere(int argc, const char **argv, const char *prefix)
{
- struct string_list merge_rr = { NULL, 0, 0, 1 };
- int i, fd, flags = 0;
-
- if (2 < argc) {
- if (!strcmp(argv[1], "-h"))
- usage(git_rerere_usage);
- if (!strcmp(argv[1], "--rerere-autoupdate"))
- flags = RERERE_AUTOUPDATE;
- else if (!strcmp(argv[1], "--no-rerere-autoupdate"))
- flags = RERERE_NOAUTOUPDATE;
- if (flags) {
- argc--;
- argv++;
- }
- }
- if (argc < 2)
+ struct string_list merge_rr = STRING_LIST_INIT_DUP;
+ int i, fd, autoupdate = -1, flags = 0;
+
+ struct option options[] = {
+ OPT_SET_INT(0, "rerere-autoupdate", &autoupdate,
+ "register clean resolutions in index", 1),
+ OPT_END(),
+ };
+
+ argc = parse_options(argc, argv, prefix, options, rerere_usage, 0);
+
+ if (autoupdate == 1)
+ flags = RERERE_AUTOUPDATE;
+ if (autoupdate == 0)
+ flags = RERERE_NOAUTOUPDATE;
+
+ if (argc < 1)
return rerere(flags);
- if (!strcmp(argv[1], "forget")) {
- const char **pathspec = get_pathspec(prefix, argv + 2);
+ if (!strcmp(argv[0], "forget")) {
+ const char **pathspec = get_pathspec(prefix, argv + 1);
return rerere_forget(pathspec);
}
@@ -129,26 +144,26 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
if (fd < 0)
return 0;
- if (!strcmp(argv[1], "clear")) {
+ if (!strcmp(argv[0], "clear")) {
for (i = 0; i < merge_rr.nr; i++) {
const char *name = (const char *)merge_rr.items[i].util;
if (!has_rerere_resolution(name))
unlink_rr_item(name);
}
- unlink_or_warn(git_path("rr-cache/MERGE_RR"));
- } else if (!strcmp(argv[1], "gc"))
+ unlink_or_warn(git_path("MERGE_RR"));
+ } else if (!strcmp(argv[0], "gc"))
garbage_collect(&merge_rr);
- else if (!strcmp(argv[1], "status"))
+ else if (!strcmp(argv[0], "status"))
for (i = 0; i < merge_rr.nr; i++)
printf("%s\n", merge_rr.items[i].string);
- else if (!strcmp(argv[1], "diff"))
+ else if (!strcmp(argv[0], "diff"))
for (i = 0; i < merge_rr.nr; i++) {
const char *path = merge_rr.items[i].string;
const char *name = (const char *)merge_rr.items[i].util;
diff_two(rerere_path(name, "preimage"), path, path, path);
}
else
- usage(git_rerere_usage);
+ usage_with_options(rerere_usage, options);
string_list_clear(&merge_rr, 1);
return 0;
diff --git a/builtin/reset.c b/builtin/reset.c
index 1283068fd2..0037be4693 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -318,7 +318,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
* affecting the working tree nor HEAD. */
if (i < argc) {
if (reset_type == MIXED)
- warning("--mixed option is deprecated with paths.");
+ warning("--mixed with paths is deprecated; use 'git reset -- <paths>' instead.");
else if (reset_type != NONE)
die("Cannot do %s reset with paths.",
reset_type_names[reset_type]);
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 51ceb19d88..efe9360e2f 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -50,6 +50,15 @@ static void show_commit(struct commit *commit, void *data)
graph_show_commit(revs->graph);
+ if (revs->count) {
+ if (commit->object.flags & SYMMETRIC_LEFT)
+ revs->count_left++;
+ else
+ revs->count_right++;
+ finish_commit(commit, data);
+ return;
+ }
+
if (info->show_timestamp)
printf("%lu ", commit->date);
if (info->header_prefix)
@@ -400,5 +409,12 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
quiet ? finish_object : show_object,
&info);
+ if (revs.count) {
+ if (revs.left_right)
+ printf("%d\t%d\n", revs.count_left, revs.count_right);
+ else
+ printf("%d\n", revs.count_left + revs.count_right);
+ }
+
return 0;
}
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 8fbf9d0db6..a5a1c86e92 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -407,8 +407,9 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
ALLOC_GROW(opts, onb + 1, osz);
memset(opts + onb, 0, sizeof(opts[onb]));
argc = parse_options(argc, argv, prefix, opts, usage,
- keep_dashdash ? PARSE_OPT_KEEP_DASHDASH : 0 |
- stop_at_non_option ? PARSE_OPT_STOP_AT_NON_OPTION : 0);
+ (keep_dashdash ? PARSE_OPT_KEEP_DASHDASH : 0) |
+ (stop_at_non_option ? PARSE_OPT_STOP_AT_NON_OPTION : 0) |
+ PARSE_OPT_SHELL_EVAL);
strbuf_addf(&parsed, " --");
sq_quote_argv(&parsed, argv, 0);
diff --git a/builtin/revert.c b/builtin/revert.c
index 778a56eb51..4b47ace36b 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -39,29 +39,34 @@ static const char * const cherry_pick_usage[] = {
static int edit, no_replay, no_commit, mainline, signoff, allow_ff;
static enum { REVERT, CHERRY_PICK } action;
static struct commit *commit;
-static const char *commit_name;
+static int commit_argc;
+static const char **commit_argv;
static int allow_rerere_auto;
static const char *me;
+static const char *strategy;
#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
static char *get_encoding(const char *message);
+static const char * const *revert_or_cherry_pick_usage(void)
+{
+ return action == REVERT ? revert_usage : cherry_pick_usage;
+}
+
static void parse_args(int argc, const char **argv)
{
- const char * const * usage_str =
- action == REVERT ? revert_usage : cherry_pick_usage;
- unsigned char sha1[20];
+ const char * const * usage_str = revert_or_cherry_pick_usage();
int noop;
struct option options[] = {
OPT_BOOLEAN('n', "no-commit", &no_commit, "don't automatically commit"),
OPT_BOOLEAN('e', "edit", &edit, "edit the commit message"),
- OPT_BOOLEAN('x', NULL, &no_replay, "append commit name when cherry-picking"),
OPT_BOOLEAN('r', NULL, &noop, "no-op (backward compatibility)"),
OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
OPT_INTEGER('m', "mainline", &mainline, "parent number"),
OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
+ OPT_STRING(0, "strategy", &strategy, "strategy", "merge strategy"),
OPT_END(),
OPT_END(),
OPT_END(),
@@ -69,6 +74,7 @@ static void parse_args(int argc, const char **argv)
if (action == CHERRY_PICK) {
struct option cp_extra[] = {
+ OPT_BOOLEAN('x', NULL, &no_replay, "append commit name"),
OPT_BOOLEAN(0, "ff", &allow_ff, "allow fast-forward"),
OPT_END(),
};
@@ -76,15 +82,13 @@ static void parse_args(int argc, const char **argv)
die("program error");
}
- if (parse_options(argc, argv, NULL, options, usage_str, 0) != 1)
+ commit_argc = parse_options(argc, argv, NULL, options, usage_str,
+ PARSE_OPT_KEEP_ARGV0 |
+ PARSE_OPT_KEEP_UNKNOWN);
+ if (commit_argc < 2)
usage_with_options(usage_str, options);
- commit_name = argv[0];
- if (get_sha1(commit_name, sha1))
- die ("Cannot find '%s'", commit_name);
- commit = lookup_commit_reference(sha1);
- if (!commit)
- exit(1);
+ commit_argv = argv;
}
struct commit_message {
@@ -98,9 +102,9 @@ struct commit_message {
static int get_message(const char *raw_message, struct commit_message *out)
{
const char *encoding;
- const char *p, *abbrev, *eol;
+ const char *abbrev, *subject;
+ int abbrev_len, subject_len;
char *q;
- int abbrev_len, oneline_len;
if (!raw_message)
return -1;
@@ -109,34 +113,29 @@ static int get_message(const char *raw_message, struct commit_message *out)
encoding = "UTF-8";
if (!git_commit_encoding)
git_commit_encoding = "UTF-8";
- if ((out->reencoded_message = reencode_string(raw_message,
- git_commit_encoding, encoding)))
+
+ out->reencoded_message = NULL;
+ out->message = raw_message;
+ if (strcmp(encoding, git_commit_encoding))
+ out->reencoded_message = reencode_string(raw_message,
+ git_commit_encoding, encoding);
+ if (out->reencoded_message)
out->message = out->reencoded_message;
abbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
abbrev_len = strlen(abbrev);
- /* Find beginning and end of commit subject. */
- p = out->message;
- while (*p && (*p != '\n' || p[1] != '\n'))
- p++;
- if (*p) {
- p += 2;
- for (eol = p + 1; *eol && *eol != '\n'; eol++)
- ; /* do nothing */
- } else
- eol = p;
- oneline_len = eol - p;
+ subject_len = find_commit_subject(out->message, &subject);
out->parent_label = xmalloc(strlen("parent of ") + abbrev_len +
- strlen("... ") + oneline_len + 1);
+ strlen("... ") + subject_len + 1);
q = out->parent_label;
q = mempcpy(q, "parent of ", strlen("parent of "));
out->label = q;
q = mempcpy(q, abbrev, abbrev_len);
q = mempcpy(q, "... ", strlen("... "));
out->subject = q;
- q = mempcpy(q, p, oneline_len);
+ q = mempcpy(q, subject, subject_len);
*q = '\0';
return 0;
}
@@ -169,28 +168,17 @@ static char *get_encoding(const char *message)
return NULL;
}
-static struct lock_file msg_file;
-static int msg_fd;
-
-static void add_to_msg(const char *string)
-{
- int len = strlen(string);
- if (write_in_full(msg_fd, string, len) < 0)
- die_errno ("Could not write to MERGE_MSG");
-}
-
-static void add_message_to_msg(const char *message)
+static void add_message_to_msg(struct strbuf *msgbuf, const char *message)
{
const char *p = message;
while (*p && (*p != '\n' || p[1] != '\n'))
p++;
if (!*p)
- add_to_msg(sha1_to_hex(commit->object.sha1));
+ strbuf_addstr(msgbuf, sha1_to_hex(commit->object.sha1));
p += 2;
- add_to_msg(p);
- return;
+ strbuf_addstr(msgbuf, p);
}
static void set_author_ident_env(const char *message)
@@ -243,27 +231,43 @@ static void set_author_ident_env(const char *message)
sha1_to_hex(commit->object.sha1));
}
-static char *help_msg(const char *name)
+static void advise(const char *advice, ...)
{
- struct strbuf helpbuf = STRBUF_INIT;
- char *msg = getenv("GIT_CHERRY_PICK_HELP");
+ va_list params;
- if (msg)
- return msg;
+ va_start(params, advice);
+ vreportf("hint: ", advice, params);
+ va_end(params);
+}
- strbuf_addstr(&helpbuf, " After resolving the conflicts,\n"
- "mark the corrected paths with 'git add <paths>' or 'git rm <paths>'\n"
- "and commit the result");
+static void print_advice(void)
+{
+ char *msg = getenv("GIT_CHERRY_PICK_HELP");
- if (action == CHERRY_PICK) {
- strbuf_addf(&helpbuf, " with: \n"
- "\n"
- " git commit -c %s\n",
- name);
+ if (msg) {
+ fprintf(stderr, "%s\n", msg);
+ return;
}
- else
- strbuf_addch(&helpbuf, '.');
- return strbuf_detach(&helpbuf, NULL);
+
+ advise("after resolving the conflicts, mark the corrected paths");
+ advise("with 'git add <paths>' or 'git rm <paths>'");
+
+ if (action == CHERRY_PICK)
+ advise("and commit the result with 'git commit -c %s'",
+ find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
+}
+
+static void write_message(struct strbuf *msgbuf, const char *filename)
+{
+ static struct lock_file msg_file;
+
+ int msg_fd = hold_lock_file_for_update(&msg_file, filename,
+ LOCK_DIE_ON_ERROR);
+ if (write_in_full(msg_fd, msgbuf->buf, msgbuf->len) < 0)
+ die_errno("Could not write to %s.", filename);
+ strbuf_release(msgbuf);
+ if (commit_lock_file(&msg_file) < 0)
+ die("Error wrapping up %s", filename);
}
static struct tree *empty_tree(void)
@@ -300,40 +304,99 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from)
return write_ref_sha1(ref_lock, to, "cherry-pick");
}
-static int revert_or_cherry_pick(int argc, const char **argv)
+static int do_recursive_merge(struct commit *base, struct commit *next,
+ const char *base_label, const char *next_label,
+ unsigned char *head, struct strbuf *msgbuf)
{
- unsigned char head[20];
- struct commit *base, *next, *parent;
- const char *base_label, *next_label;
- int i, index_fd, clean;
- struct commit_message msg = { NULL, NULL, NULL, NULL, NULL };
- char *defmsg = NULL;
struct merge_options o;
struct tree *result, *next_tree, *base_tree, *head_tree;
+ int clean, index_fd;
static struct lock_file index_lock;
- git_config(git_default_config, NULL);
- me = action == REVERT ? "revert" : "cherry-pick";
- setenv(GIT_REFLOG_ACTION, me, 0);
- parse_args(argc, argv);
+ index_fd = hold_locked_index(&index_lock, 1);
- /* this is copied from the shell script, but it's never triggered... */
- if (action == REVERT && !no_replay)
- die("revert is incompatible with replay");
+ read_cache();
- if (allow_ff) {
- if (signoff)
- die("cherry-pick --ff cannot be used with --signoff");
- if (no_commit)
- die("cherry-pick --ff cannot be used with --no-commit");
- if (no_replay)
- die("cherry-pick --ff cannot be used with -x");
- if (edit)
- die("cherry-pick --ff cannot be used with --edit");
+ /*
+ * NEEDSWORK: cherry-picking between branches with
+ * different end-of-line normalization is a pain;
+ * plumb in an option to set o.renormalize?
+ * (or better: arbitrary -X options)
+ */
+ init_merge_options(&o);
+ o.ancestor = base ? base_label : "(empty tree)";
+ o.branch1 = "HEAD";
+ o.branch2 = next ? next_label : "(empty tree)";
+
+ head_tree = parse_tree_indirect(head);
+ next_tree = next ? next->tree : empty_tree();
+ base_tree = base ? base->tree : empty_tree();
+
+ clean = merge_trees(&o,
+ head_tree,
+ next_tree, base_tree, &result);
+
+ if (active_cache_changed &&
+ (write_cache(index_fd, active_cache, active_nr) ||
+ commit_locked_index(&index_lock)))
+ die("%s: Unable to write new index file", me);
+ rollback_lock_file(&index_lock);
+
+ if (!clean) {
+ int i;
+ strbuf_addstr(msgbuf, "\nConflicts:\n\n");
+ for (i = 0; i < active_nr;) {
+ struct cache_entry *ce = active_cache[i++];
+ if (ce_stage(ce)) {
+ strbuf_addch(msgbuf, '\t');
+ strbuf_addstr(msgbuf, ce->name);
+ strbuf_addch(msgbuf, '\n');
+ while (i < active_nr && !strcmp(ce->name,
+ active_cache[i]->name))
+ i++;
+ }
+ }
}
- if (read_cache() < 0)
- die("git %s: failed to read the index", me);
+ return !clean;
+}
+
+/*
+ * If we are cherry-pick, and if the merge did not result in
+ * hand-editing, we will hit this commit and inherit the original
+ * author date and name.
+ * If we are revert, or if our cherry-pick results in a hand merge,
+ * we had better say that the current user is responsible for that.
+ */
+static int run_git_commit(const char *defmsg)
+{
+ /* 6 is max possible length of our args array including NULL */
+ const char *args[6];
+ int i = 0;
+
+ args[i++] = "commit";
+ args[i++] = "-n";
+ if (signoff)
+ args[i++] = "-s";
+ if (!edit) {
+ args[i++] = "-F";
+ args[i++] = defmsg;
+ }
+ args[i] = NULL;
+
+ return run_command_v_opt(args, RUN_GIT_CMD);
+}
+
+static int do_pick_commit(void)
+{
+ unsigned char head[20];
+ struct commit *base, *next, *parent;
+ const char *base_label, *next_label;
+ struct commit_message msg = { NULL, NULL, NULL, NULL, NULL };
+ char *defmsg = NULL;
+ struct strbuf msgbuf = STRBUF_INIT;
+ int res;
+
if (no_commit) {
/*
* We do not intend to commit immediately. We just want to
@@ -398,110 +461,122 @@ static int revert_or_cherry_pick(int argc, const char **argv)
*/
defmsg = git_pathdup("MERGE_MSG");
- msg_fd = hold_lock_file_for_update(&msg_file, defmsg,
- LOCK_DIE_ON_ERROR);
-
- index_fd = hold_locked_index(&index_lock, 1);
if (action == REVERT) {
base = commit;
base_label = msg.label;
next = parent;
next_label = msg.parent_label;
- add_to_msg("Revert \"");
- add_to_msg(msg.subject);
- add_to_msg("\"\n\nThis reverts commit ");
- add_to_msg(sha1_to_hex(commit->object.sha1));
+ strbuf_addstr(&msgbuf, "Revert \"");
+ strbuf_addstr(&msgbuf, msg.subject);
+ strbuf_addstr(&msgbuf, "\"\n\nThis reverts commit ");
+ strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
if (commit->parents->next) {
- add_to_msg(", reversing\nchanges made to ");
- add_to_msg(sha1_to_hex(parent->object.sha1));
+ strbuf_addstr(&msgbuf, ", reversing\nchanges made to ");
+ strbuf_addstr(&msgbuf, sha1_to_hex(parent->object.sha1));
}
- add_to_msg(".\n");
+ strbuf_addstr(&msgbuf, ".\n");
} else {
base = parent;
base_label = msg.parent_label;
next = commit;
next_label = msg.label;
set_author_ident_env(msg.message);
- add_message_to_msg(msg.message);
+ add_message_to_msg(&msgbuf, msg.message);
if (no_replay) {
- add_to_msg("(cherry picked from commit ");
- add_to_msg(sha1_to_hex(commit->object.sha1));
- add_to_msg(")\n");
+ strbuf_addstr(&msgbuf, "(cherry picked from commit ");
+ strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
+ strbuf_addstr(&msgbuf, ")\n");
}
}
- read_cache();
- init_merge_options(&o);
- o.ancestor = base ? base_label : "(empty tree)";
- o.branch1 = "HEAD";
- o.branch2 = next ? next_label : "(empty tree)";
-
- head_tree = parse_tree_indirect(head);
- next_tree = next ? next->tree : empty_tree();
- base_tree = base ? base->tree : empty_tree();
+ if (!strategy || !strcmp(strategy, "recursive") || action == REVERT) {
+ res = do_recursive_merge(base, next, base_label, next_label,
+ head, &msgbuf);
+ write_message(&msgbuf, defmsg);
+ } else {
+ struct commit_list *common = NULL;
+ struct commit_list *remotes = NULL;
- clean = merge_trees(&o,
- head_tree,
- next_tree, base_tree, &result);
+ write_message(&msgbuf, defmsg);
- if (active_cache_changed &&
- (write_cache(index_fd, active_cache, active_nr) ||
- commit_locked_index(&index_lock)))
- die("%s: Unable to write new index file", me);
- rollback_lock_file(&index_lock);
+ commit_list_insert(base, &common);
+ commit_list_insert(next, &remotes);
+ res = try_merge_command(strategy, common,
+ sha1_to_hex(head), remotes);
+ free_commit_list(common);
+ free_commit_list(remotes);
+ }
- if (!clean) {
- add_to_msg("\nConflicts:\n\n");
- for (i = 0; i < active_nr;) {
- struct cache_entry *ce = active_cache[i++];
- if (ce_stage(ce)) {
- add_to_msg("\t");
- add_to_msg(ce->name);
- add_to_msg("\n");
- while (i < active_nr && !strcmp(ce->name,
- active_cache[i]->name))
- i++;
- }
- }
- if (commit_lock_file(&msg_file) < 0)
- die ("Error wrapping up %s", defmsg);
- fprintf(stderr, "Automatic %s failed.%s\n",
- me, help_msg(commit_name));
+ if (res) {
+ error("could not %s %s... %s",
+ action == REVERT ? "revert" : "apply",
+ find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV),
+ msg.subject);
+ print_advice();
rerere(allow_rerere_auto);
- exit(1);
+ } else {
+ if (!no_commit)
+ res = run_git_commit(defmsg);
}
- if (commit_lock_file(&msg_file) < 0)
- die ("Error wrapping up %s", defmsg);
- fprintf(stderr, "Finished one %s.\n", me);
- /*
- *
- * If we are cherry-pick, and if the merge did not result in
- * hand-editing, we will hit this commit and inherit the original
- * author date and name.
- * If we are revert, or if our cherry-pick results in a hand merge,
- * we had better say that the current user is responsible for that.
- */
+ free_message(&msg);
+ free(defmsg);
+
+ return res;
+}
+
+static void prepare_revs(struct rev_info *revs)
+{
+ int argc;
- if (!no_commit) {
- /* 6 is max possible length of our args array including NULL */
- const char *args[6];
- int i = 0;
- args[i++] = "commit";
- args[i++] = "-n";
+ init_revisions(revs, NULL);
+ revs->no_walk = 1;
+ if (action != REVERT)
+ revs->reverse = 1;
+
+ argc = setup_revisions(commit_argc, commit_argv, revs, NULL);
+ if (argc > 1)
+ usage(*revert_or_cherry_pick_usage());
+
+ if (prepare_revision_walk(revs))
+ die("revision walk setup failed");
+
+ if (!revs->commits)
+ die("empty commit set passed");
+}
+
+static int revert_or_cherry_pick(int argc, const char **argv)
+{
+ struct rev_info revs;
+
+ git_config(git_default_config, NULL);
+ me = action == REVERT ? "revert" : "cherry-pick";
+ setenv(GIT_REFLOG_ACTION, me, 0);
+ parse_args(argc, argv);
+
+ if (allow_ff) {
if (signoff)
- args[i++] = "-s";
- if (!edit) {
- args[i++] = "-F";
- args[i++] = defmsg;
- }
- args[i] = NULL;
- return execv_git_cmd(args);
+ die("cherry-pick --ff cannot be used with --signoff");
+ if (no_commit)
+ die("cherry-pick --ff cannot be used with --no-commit");
+ if (no_replay)
+ die("cherry-pick --ff cannot be used with -x");
+ if (edit)
+ die("cherry-pick --ff cannot be used with --edit");
+ }
+
+ if (read_cache() < 0)
+ die("git %s: failed to read the index", me);
+
+ prepare_revs(&revs);
+
+ while ((commit = get_revision(&revs))) {
+ int res = do_pick_commit();
+ if (res)
+ return res;
}
- free_message(&msg);
- free(defmsg);
return 0;
}
@@ -510,14 +585,12 @@ int cmd_revert(int argc, const char **argv, const char *prefix)
{
if (isatty(0))
edit = 1;
- no_replay = 1;
action = REVERT;
return revert_or_cherry_pick(argc, argv);
}
int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
{
- no_replay = 0;
action = CHERRY_PICK;
return revert_or_cherry_pick(argc, argv);
}
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index 06320f5285..2135b0dde1 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -84,7 +84,7 @@ static void insert_one_record(struct shortlog *log,
snprintf(namebuf + len, room, " <%.*s>", maillen, emailbuf);
}
- item = string_list_insert(namebuf, &log->list);
+ item = string_list_insert(&log->list, namebuf);
if (item->util == NULL)
item->util = xcalloc(1, sizeof(struct string_list));
@@ -115,7 +115,7 @@ static void insert_one_record(struct shortlog *log,
}
}
- string_list_append(buffer, item->util);
+ string_list_append(item->util, buffer);
}
static void read_from_stdin(struct shortlog *log)
@@ -162,7 +162,7 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
sha1_to_hex(commit->object.sha1));
if (log->user_format) {
struct pretty_print_context ctx = {0};
- ctx.abbrev = DEFAULT_ABBREV;
+ ctx.abbrev = log->abbrev;
ctx.subject = "";
ctx.after_subject = "";
ctx.date_mode = DATE_NORMAL;
@@ -249,7 +249,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
{
static struct shortlog log;
static struct rev_info rev;
- int nongit;
+ int nongit = !startup_info->have_repository;
static const struct option options[] = {
OPT_BOOLEAN('n', "numbered", &log.sort_by_number,
@@ -265,7 +265,6 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
struct parse_opt_ctx_t ctx;
- prefix = setup_git_directory_gently(&nongit);
git_config(git_default_config, NULL);
shortlog_init(&log);
init_revisions(&rev, prefix);
@@ -290,6 +289,7 @@ parse_done:
}
log.user_format = rev.commit_format == CMIT_FMT_USERFORMAT;
+ log.abbrev = rev.abbrev;
/* assume HEAD if from a tty */
if (!nongit && !rev.pending.nr && isatty(0))
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index e20fcf3e93..e8719aa9e9 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -313,7 +313,8 @@ static void show_one_commit(struct commit *commit, int no_name)
}
else
printf("[%s] ",
- find_unique_abbrev(commit->object.sha1, 7));
+ find_unique_abbrev(commit->object.sha1,
+ DEFAULT_ABBREV));
}
puts(pretty_str);
strbuf_release(&pretty);
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 17ada88dfb..be9b512eeb 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -105,7 +105,7 @@ match:
static int add_existing(const char *refname, const unsigned char *sha1, int flag, void *cbdata)
{
struct string_list *list = (struct string_list *)cbdata;
- string_list_insert(refname, list);
+ string_list_insert(list, refname);
return 0;
}
@@ -120,7 +120,7 @@ static int add_existing(const char *refname, const unsigned char *sha1, int flag
*/
static int exclude_existing(const char *match)
{
- static struct string_list existing_refs = { NULL, 0, 0, 0 };
+ static struct string_list existing_refs = STRING_LIST_INIT_NODUP;
char buf[1024];
int matchlen = match ? strlen(match) : 0;
diff --git a/builtin/var.c b/builtin/var.c
index 70fdb4dec7..0744bb8318 100644
--- a/builtin/var.c
+++ b/builtin/var.c
@@ -74,14 +74,9 @@ static int show_config(const char *var, const char *value, void *cb)
int cmd_var(int argc, const char **argv, const char *prefix)
{
- const char *val;
- int nongit;
- if (argc != 2) {
+ const char *val = NULL;
+ if (argc != 2)
usage(var_usage);
- }
-
- setup_git_directory_gently(&nongit);
- val = NULL;
if (strcmp(argv[1], "-l") == 0) {
git_config(show_config, NULL);