*.tex diff=tex
------------------------
- Then, you would define "diff.tex.funcname" configuration to
+ Then, you would define "diff.tex.xfuncname" configuration to
specify a regular expression that matches a line that you would
want to appear as the hunk header, like this:
------------------------
[diff "tex"]
- funcname = "^\\(\\\\\\(sub\\)*section{.*\\)$"
+ xfuncname = "^(\\\\(sub)*section\\{.*)$"
------------------------
Note. A single level of backslashes are eaten by the
- `bibtex` suitable for files with BibTeX coded references.
- - `java` suitable for source code in the Java lanugage.
+- `html` suitable for HTML/XHTML documents.
+
+ - `java` suitable for source code in the Java language.
- `pascal` suitable for source code in the Pascal/Delphi language.
+- `php` suitable for source code in the PHP language.
+
+- `python` suitable for source code in the Python language.
+
- `ruby` suitable for source code in the Ruby language.
- `tex` suitable for source code for LaTeX documents.
# Define USE_STDEV below if you want git to care about the underlying device
# change being considered an inode change from the update-index perspective.
#
+# Define NO_ST_BLOCKS_IN_STRUCT_STAT if your platform does not have st_blocks
+# field that counts the on-disk footprint in 512-byte blocks.
+#
# Define ASCIIDOC8 if you want to format documentation with AsciiDoc 8
#
# Define DOCBOOK_XSL_172 if you want to format man pages with DocBook XSL v1.72.
LIB_H += graph.h
LIB_H += grep.h
LIB_H += hash.h
+LIB_H += help.h
LIB_H += list-objects.h
LIB_H += ll-merge.h
LIB_H += log-tree.h
BUILTIN_OBJS += builtin-fsck.o
BUILTIN_OBJS += builtin-gc.o
BUILTIN_OBJS += builtin-grep.o
+BUILTIN_OBJS += builtin-help.o
BUILTIN_OBJS += builtin-init-db.o
BUILTIN_OBJS += builtin-log.o
BUILTIN_OBJS += builtin-ls-files.o
ifeq ($(uname_S),Linux)
NO_STRLCPY = YesPlease
+ THREADED_DELTA_SEARCH = YesPlease
endif
ifeq ($(uname_S),GNU/kFreeBSD)
NO_STRLCPY = YesPlease
+ THREADED_DELTA_SEARCH = YesPlease
endif
ifeq ($(uname_S),UnixWare)
CC = cc
endif
NO_STRLCPY = YesPlease
NO_MEMMEM = YesPlease
+ COMPAT_CFLAGS += -Icompat/regex
+ COMPAT_OBJS += compat/regex/regex.o
endif
ifeq ($(uname_S),SunOS)
NEEDS_SOCKET = YesPlease
BASIC_CFLAGS += -I/usr/local/include
BASIC_LDFLAGS += -L/usr/local/lib
DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease
+ THREADED_DELTA_SEARCH = YesPlease
+ COMPAT_CFLAGS += -Icompat/regex
+ COMPAT_OBJS += compat/regex/regex.o
endif
ifeq ($(uname_S),OpenBSD)
NO_STRCASESTR = YesPlease
NEEDS_LIBICONV = YesPlease
BASIC_CFLAGS += -I/usr/local/include
BASIC_LDFLAGS += -L/usr/local/lib
+ THREADED_DELTA_SEARCH = YesPlease
endif
ifeq ($(uname_S),NetBSD)
ifeq ($(shell expr "$(uname_R)" : '[01]\.'),2)
BASIC_CFLAGS += -I/usr/pkg/include
BASIC_LDFLAGS += -L/usr/pkg/lib
ALL_LDFLAGS += -Wl,-rpath,/usr/pkg/lib
+ THREADED_DELTA_SEARCH = YesPlease
endif
ifeq ($(uname_S),AIX)
NO_STRCASESTR=YesPlease
INTERNAL_QSORT = UnfortunatelyYes
NEEDS_LIBICONV=YesPlease
BASIC_CFLAGS += -D_LARGE_FILES
+ COMPAT_CFLAGS += -Icompat/regex
+ COMPAT_OBJS += compat/regex/regex.o
endif
ifeq ($(uname_S),GNU)
# GNU/Hurd
NO_SVN_TESTS = YesPlease
NO_PERL_MAKEMAKER = YesPlease
NO_POSIX_ONLY_PROGRAMS = YesPlease
- COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat
+ NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
+ COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/regex -Icompat/fnmatch
COMPAT_CFLAGS += -DSNPRINTF_SIZE_CORR=1
COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
- COMPAT_OBJS += compat/mingw.o compat/fnmatch.o compat/regex.o compat/winansi.o
+ COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/regex/regex.o compat/winansi.o
EXTLIBS += -lws2_32
X = .exe
gitexecdir = ../libexec/git-core
ifdef NO_D_INO_IN_DIRENT
BASIC_CFLAGS += -DNO_D_INO_IN_DIRENT
endif
+ifdef NO_ST_BLOCKS_IN_STRUCT_STAT
+ BASIC_CFLAGS += -DNO_ST_BLOCKS_IN_STRUCT_STAT
+endif
ifdef NO_C99_FORMAT
BASIC_CFLAGS += -DNO_C99_FORMAT
endif
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ git.o \
$(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)
-help.o: help.c common-cmds.h GIT-CFLAGS
+builtin-help.o: builtin-help.c common-cmds.h GIT-CFLAGS
$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \
'-DGIT_HTML_PATH="$(htmldir_SQ)"' \
'-DGIT_MAN_PATH="$(mandir_SQ)"' \
git-%$X: %.o $(GITLIBS)
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
-git-imap-send$X: imap-send.o $(LIB_FILE)
+git-imap-send$X: imap-send.o $(GITLIBS)
+ $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
+ $(LIBS) $(OPENSSL_LINK) $(OPENSSL_LIBSSL)
http.o http-walker.o http-push.o transport.o: http.h
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
$(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
- $(INSTALL) git$X git-upload-pack$X git-receive-pack$X git-upload-archive$X '$(DESTDIR_SQ)$(bindir_SQ)'
+ $(INSTALL) git$X git-upload-pack$X git-receive-pack$X git-upload-archive$X git-shell$X '$(DESTDIR_SQ)$(bindir_SQ)'
$(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
$(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
ifndef NO_TCLTK
return 0;
}
+ static int skip_same_name(struct cache_entry *ce, int pos)
+ {
+ while (++pos < active_nr &&
+ !strcmp(active_cache[pos]->name, ce->name))
+ ; /* skip */
+ return pos;
+ }
+
+
static int checkout_paths(struct tree *source_tree, const char **pathspec)
{
int pos;
if (report_path_error(ps_matched, pathspec, 0))
return 1;
+ /* Any unmerged paths? */
+ for (pos = 0; pos < active_nr; pos++) {
+ struct cache_entry *ce = active_cache[pos];
+ if (pathspec_match(pathspec, NULL, ce->name, 0)) {
+ if (!ce_stage(ce))
+ continue;
+ errs = 1;
+ error("path '%s' is unmerged", ce->name);
+ pos = skip_same_name(ce, pos) - 1;
+ }
+ }
+ if (errs)
+ return 1;
+
/* Now we are committed to check them out */
memset(&state, 0, sizeof(state));
state.force = 1;
for (pos = 0; pos < active_nr; pos++) {
struct cache_entry *ce = active_cache[pos];
if (pathspec_match(pathspec, NULL, ce->name, 0)) {
- errs |= checkout_entry(ce, &state, NULL);
+ if (!ce_stage(ce)) {
+ errs |= checkout_entry(ce, &state, NULL);
+ continue;
+ }
+ pos = skip_same_name(ce, pos) - 1;
}
}
int force;
int writeout_error;
- char *new_branch;
+ const char *new_branch;
int new_branch_log;
enum branch_track track;
};
git_config(git_default_config, NULL);
- opts.track = git_branch_track;
+ opts.track = BRANCH_TRACK_UNSPECIFIED;
argc = parse_options(argc, argv, options, checkout_usage,
PARSE_OPT_KEEP_DASHDASH);
- if (!opts.new_branch && (opts.track != git_branch_track))
- die("git checkout: --track and --no-track require -b");
+ /* --track without -b should DWIM */
+ if (0 < opts.track && !opts.new_branch) {
+ const char *argv0 = argv[0];
+ if (!argc || !strcmp(argv0, "--"))
+ die ("--track needs a branch name");
+ if (!prefixcmp(argv0, "refs/"))
+ argv0 += 5;
+ if (!prefixcmp(argv0, "remotes/"))
+ argv0 += 8;
+ argv0 = strchr(argv0, '/');
+ if (!argv0 || !argv0[1])
+ die ("Missing branch name; try -b");
+ opts.new_branch = argv0 + 1;
+ }
+
+ if (opts.track == BRANCH_TRACK_UNSPECIFIED)
+ opts.track = git_branch_track;
if (opts.force && opts.merge)
die("git checkout: -f and -m are incompatible");
typename(expect));
}
- static const char commit_tree_usage[] = "git-commit-tree <sha1> [-p <sha1>]* < changelog";
+ 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)
{
int commit_tree(const char *msg, unsigned char *tree,
struct commit_list *parents, unsigned char *ret)
{
+ int result;
int encoding_is_utf8;
struct strbuf buffer;
if (encoding_is_utf8 && !is_utf8(buffer.buf))
fprintf(stderr, commit_utf8_warn);
- return write_sha1_file(buffer.buf, buffer.len, commit_type, ret);
+ 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)
*av++ = "--fix-thin";
if (args.lock_pack || unpack_limit) {
int s = sprintf(keep_arg,
- "--keep=fetch-pack %d on ", getpid());
+ "--keep=fetch-pack %"PRIuMAX " on ", (uintmax_t) getpid());
if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
strcpy(keep_arg + s, "localhost");
*av++ = keep_arg;
if (!ret && nr_heads) {
/* If the heads to pull were given, we should have
* consumed all of them by matching the remote.
- * Otherwise, 'git-fetch remote no-such-ref' would
+ * Otherwise, 'git fetch remote no-such-ref' would
* silently succeed without issuing an error.
*/
for (i = 0; i < nr_heads; i++)
* Default to not allowing changes to the list of files. The
* tool doesn't actually care, but this makes it harder to add
* files to the revision control by mistake by doing something
- * like "git-update-index *" and suddenly having all the object
+ * like "git update-index *" and suddenly having all the object
* files be revision controlled.
*/
static int allow_add;
int len;
struct stat st;
+ len = strlen(path);
+ if (has_symlink_leading_path(len, path))
+ return error("'%s' is beyond a symbolic link", path);
+
/*
* First things first: get the stat information, to decide
* what to do about the pathname!
if (lstat(path, &st) < 0)
return process_lstat_error(path, errno);
- len = strlen(path);
if (S_ISDIR(st.st_mode))
return process_directory(path, len, &st);
/* This reads lines formatted in one of three formats:
*
* (1) mode SP sha1 TAB path
- * The first format is what "git-apply --index-info"
+ * The first format is what "git apply --index-info"
* reports, and used to reconstruct a partial tree
* that is used for phony merge base tree when falling
* back on 3-way merge.
*
* (2) mode SP type SP sha1 TAB path
- * The second format is to stuff git-ls-tree output
+ * The second format is to stuff "git ls-tree" output
* into the index file.
*
* (3) mode SP sha1 SP stage TAB path
* This format is to put higher order stages into the
- * index file and matches git-ls-files --stage output.
+ * index file and matches "git ls-files --stage" output.
*/
errno = 0;
ul = strtoul(buf.buf, &ptr, 8);
}
struct combine_diff_state {
- struct xdiff_emit_state xm;
-
unsigned int lno;
int ob, on, nb, nn;
unsigned long nmask;
parent_file.size = sz;
xpp.flags = XDF_NEED_MINIMAL;
memset(&xecfg, 0, sizeof(xecfg));
- ecb.outf = xdiff_outf;
- ecb.priv = &state;
memset(&state, 0, sizeof(state));
- state.xm.consume = consume_line;
state.nmask = nmask;
state.sline = sline;
state.lno = 1;
state.num_parent = num_parent;
state.n = n;
- xdi_diff(&parent_file, result_file, &xpp, &xecfg, &ecb);
+ xdi_diff_outf(&parent_file, result_file, consume_line, &state,
+ &xpp, &xecfg, &ecb);
free(parent_file.ptr);
/* Assign line numbers for this parent.
return (isalpha(ch) || ch == '_' || ch == '$');
}
+ static void show_line_to_eol(const char *line, int len, const char *reset)
+ {
+ int saw_cr_at_eol = 0;
+ if (len < 0)
+ len = strlen(line);
+ saw_cr_at_eol = (len && line[len-1] == '\r');
+
+ printf("%.*s%s%s\n", len - saw_cr_at_eol, line,
+ reset,
+ saw_cr_at_eol ? "\r" : "");
+ }
+
static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent,
int use_color)
{
else
putchar(' ');
}
- printf("%s%s\n", ll->line, c_reset);
+ show_line_to_eol(ll->line, -1, c_reset);
ll = ll->next;
}
if (cnt < lno)
putchar(' ');
p_mask <<= 1;
}
- printf("%.*s%s\n", sl->len, sl->bol, c_reset);
+ show_line_to_eol(sl->bol, sl->len, c_reset);
}
}
}
static int diff_detect_rename_default;
static int diff_rename_limit_default = 200;
+static int diff_suppress_blank_empty;
int diff_use_color_default = -1;
static const char *external_diff_cmd_cfg;
int diff_auto_refresh_index = 1;
* to define a customized regexp to find the beginning of a function to
* be used for hunk header lines of "diff -p" style output.
*/
- static struct funcname_pattern {
+ struct funcname_pattern_entry {
char *name;
char *pattern;
- struct funcname_pattern *next;
+ int cflags;
+ };
+ static struct funcname_pattern_list {
+ struct funcname_pattern_list *next;
+ struct funcname_pattern_entry e;
} *funcname_pattern_list;
- static int parse_funcname_pattern(const char *var, const char *ep, const char *value)
+ static int parse_funcname_pattern(const char *var, const char *ep, const char *value, int cflags)
{
const char *name;
int namelen;
- struct funcname_pattern *pp;
+ struct funcname_pattern_list *pp;
name = var + 5; /* "diff." */
namelen = ep - name;
for (pp = funcname_pattern_list; pp; pp = pp->next)
- if (!strncmp(pp->name, name, namelen) && !pp->name[namelen])
+ if (!strncmp(pp->e.name, name, namelen) && !pp->e.name[namelen])
break;
if (!pp) {
pp = xcalloc(1, sizeof(*pp));
- pp->name = xmemdupz(name, namelen);
+ pp->e.name = xmemdupz(name, namelen);
pp->next = funcname_pattern_list;
funcname_pattern_list = pp;
}
- free(pp->pattern);
- pp->pattern = xstrdup(value);
+ free(pp->e.pattern);
+ pp->e.pattern = xstrdup(value);
+ pp->e.cflags = cflags;
return 0;
}
return 0;
}
+ /* like GNU diff's --suppress-blank-empty option */
+ if (!strcmp(var, "diff.suppress-blank-empty")) {
+ diff_suppress_blank_empty = git_config_bool(var, value);
+ return 0;
+ }
+
if (!prefixcmp(var, "diff.")) {
const char *ep = strrchr(var, '.');
if (ep != var + 4) {
if (!strcmp(ep, ".funcname")) {
if (!value)
return config_error_nonbool(var);
- return parse_funcname_pattern(var, ep, value);
+ return parse_funcname_pattern(var, ep, value,
+ 0);
+ } else if (!strcmp(ep, ".xfuncname")) {
+ if (!value)
+ return config_error_nonbool(var);
+ return parse_funcname_pattern(var, ep, value,
+ REG_EXTENDED);
}
}
}
}
struct diff_words_data {
- struct xdiff_emit_state xm;
struct diff_words_buffer minus, plus;
FILE *file;
};
xpp.flags = XDF_NEED_MINIMAL;
xecfg.ctxlen = diff_words->minus.alloc + diff_words->plus.alloc;
- ecb.outf = xdiff_outf;
- ecb.priv = diff_words;
- diff_words->xm.consume = fn_out_diff_words_aux;
- xdi_diff(&minus, &plus, &xpp, &xecfg, &ecb);
-
+ xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words,
+ &xpp, &xecfg, &ecb);
free(minus.ptr);
free(plus.ptr);
diff_words->minus.text.size = diff_words->plus.text.size = 0;
typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len);
struct emit_callback {
- struct xdiff_emit_state xm;
int nparents, color_diff;
unsigned ws_rule;
sane_truncate_fn truncate;
static void emit_line(FILE *file, const char *set, const char *reset, const char *line, int len)
{
- int has_trailing_newline = (len > 0 && line[len-1] == '\n');
+ int has_trailing_newline, has_trailing_carriage_return;
+
+ has_trailing_newline = (len > 0 && line[len-1] == '\n');
if (has_trailing_newline)
len--;
+ has_trailing_carriage_return = (len > 0 && line[len-1] == '\r');
+ if (has_trailing_carriage_return)
+ len--;
fputs(set, file);
fwrite(line, len, 1, file);
fputs(reset, file);
+ if (has_trailing_carriage_return)
+ fputc('\r', file);
if (has_trailing_newline)
fputc('\n', file);
}
ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
}
+ if (diff_suppress_blank_empty
+ && len == 2 && line[0] == ' ' && line[1] == '\n') {
+ line[0] = '\n';
+ len = 1;
+ }
+
/* This is not really necessary for now because
* this codepath only deals with two-way diffs.
*/
}
struct diffstat_t {
- struct xdiff_emit_state xm;
-
int nr;
int alloc;
struct diffstat_file {
}
struct checkdiff_t {
- struct xdiff_emit_state xm;
const char *filename;
int lineno;
struct diff_options *o;
return one->is_binary;
}
- static const char *funcname_pattern(const char *ident)
+ static const struct funcname_pattern_entry *funcname_pattern(const char *ident)
{
- struct funcname_pattern *pp;
+ struct funcname_pattern_list *pp;
for (pp = funcname_pattern_list; pp; pp = pp->next)
- if (!strcmp(ident, pp->name))
- return pp->pattern;
+ if (!strcmp(ident, pp->e.name))
+ return &pp->e;
return NULL;
}
- static struct builtin_funcname_pattern {
- const char *name;
- const char *pattern;
- } builtin_funcname_pattern[] = {
- { "bibtex", "\\(@[a-zA-Z]\\{1,\\}[ \t]*{\\{0,1\\}[ \t]*[^ \t\"@',\\#}{~%]*\\).*$" },
- { "html", "^\\s*\\(<[Hh][1-6]\\s.*>.*\\)$" },
+ static const struct funcname_pattern_entry builtin_funcname_pattern[] = {
++ { "bibtex", "\\(@[a-zA-Z]\\{1,\\}[ \t]*{\\{0,1\\}[ \t]*[^ \t\"@',\\#}{~%]*\\).*$", 0 },
++ { "html", "^\\s*\\(<[Hh][1-6]\\s.*>.*\\)$", 0 },
{ "java", "!^[ ]*\\(catch\\|do\\|for\\|if\\|instanceof\\|"
"new\\|return\\|switch\\|throw\\|while\\)\n"
"^[ ]*\\(\\([ ]*"
"[A-Za-z_][A-Za-z_0-9]*\\)\\{2,\\}"
- "[ ]*([^;]*\\)$" },
+ "[ ]*([^;]*\\)$", 0 },
{ "pascal", "^\\(\\(procedure\\|function\\|constructor\\|"
"destructor\\|interface\\|implementation\\|"
"initialization\\|finalization\\)[ \t]*.*\\)$"
"\\|"
- "^\\(.*=[ \t]*\\(class\\|record\\).*\\)$"
- },
- { "php", "^[\t ]*\\(\\(function\\|class\\).*\\)" },
- { "python", "^\\s*\\(\\(class\\|def\\)\\s.*\\)$" },
- { "ruby", "^\\s*\\(\\(class\\|module\\|def\\)\\s.*\\)$" },
- { "tex", "^\\(\\\\\\(\\(sub\\)*section\\|chapter\\|part\\)\\*\\{0,1\\}{.*\\)$" },
+ "^\\(.*=[ \t]*\\(class\\|record\\).*\\)$",
+ 0 },
- { "bibtex", "\\(@[a-zA-Z]\\{1,\\}[ \t]*{\\{0,1\\}[ \t]*[^ \t\"@',\\#}{~%]*\\).*$", 0 },
- { "tex", "^\\(\\\\\\(\\(sub\\)*section\\|chapter\\|part\\)\\*\\{0,1\\}{.*\\)$", 0 },
++ { "php", "^[\t ]*\\(\\(function\\|class\\).*\\)", 0 },
++ { "python", "^\\s*\\(\\(class\\|def\\)\\s.*\\)$", 0 },
+ { "ruby", "^\\s*\\(\\(class\\|module\\|def\\)\\s.*\\)$", 0 },
++ { "tex", "^\\(\\\\\\(\\(sub\\)*section\\|chapter\\|part\\)\\*\\{0,1\\}{.*\\)$", 0 },
};
- static const char *diff_funcname_pattern(struct diff_filespec *one)
+ static const struct funcname_pattern_entry *diff_funcname_pattern(struct diff_filespec *one)
{
- const char *ident, *pattern;
+ const char *ident;
+ const struct funcname_pattern_entry *pe;
int i;
diff_filespec_check_attr(one);
return funcname_pattern("default");
/* Look up custom "funcname.$ident" regexp from config. */
- pattern = funcname_pattern(ident);
- if (pattern)
- return pattern;
+ pe = funcname_pattern(ident);
+ if (pe)
+ return pe;
/*
* And define built-in fallback patterns here. Note that
*/
for (i = 0; i < ARRAY_SIZE(builtin_funcname_pattern); i++)
if (!strcmp(ident, builtin_funcname_pattern[i].name))
- return builtin_funcname_pattern[i].pattern;
+ return &builtin_funcname_pattern[i];
return NULL;
}
xdemitconf_t xecfg;
xdemitcb_t ecb;
struct emit_callback ecbdata;
- const char *funcname_pattern;
+ const struct funcname_pattern_entry *pe;
- funcname_pattern = diff_funcname_pattern(one);
- if (!funcname_pattern)
- funcname_pattern = diff_funcname_pattern(two);
+ pe = diff_funcname_pattern(one);
+ if (!pe)
+ pe = diff_funcname_pattern(two);
memset(&xecfg, 0, sizeof(xecfg));
memset(&ecbdata, 0, sizeof(ecbdata));
xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
xecfg.ctxlen = o->context;
xecfg.flags = XDL_EMIT_FUNCNAMES;
- if (funcname_pattern)
- xdiff_set_find_func(&xecfg, funcname_pattern);
+ if (pe)
+ xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
if (!diffopts)
;
else if (!prefixcmp(diffopts, "--unified="))
xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
else if (!prefixcmp(diffopts, "-u"))
xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
- ecb.outf = xdiff_outf;
- ecb.priv = &ecbdata;
- ecbdata.xm.consume = fn_out_consume;
if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) {
ecbdata.diff_words =
xcalloc(1, sizeof(struct diff_words_data));
ecbdata.diff_words->file = o->file;
}
- xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+ xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata,
+ &xpp, &xecfg, &ecb);
if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS))
free_diff_words_data(&ecbdata);
}
memset(&xecfg, 0, sizeof(xecfg));
xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
- ecb.outf = xdiff_outf;
- ecb.priv = diffstat;
- xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+ xdi_diff_outf(&mf1, &mf2, diffstat_consume, diffstat,
+ &xpp, &xecfg, &ecb);
}
free_and_return:
return;
memset(&data, 0, sizeof(data));
- data.xm.consume = checkdiff_consume;
data.filename = name_b ? name_b : name_a;
data.lineno = 0;
data.o = o;
memset(&xecfg, 0, sizeof(xecfg));
xecfg.ctxlen = 1; /* at least one context line */
xpp.flags = XDF_NEED_MINIMAL;
- ecb.outf = xdiff_outf;
- ecb.priv = &data;
- xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+ xdi_diff_outf(&mf1, &mf2, checkdiff_consume, &data,
+ &xpp, &xecfg, &ecb);
if ((data.ws_rule & WS_TRAILING_SPACE) &&
data.trailing_blanks_start) {
}
struct patch_id_t {
- struct xdiff_emit_state xm;
SHA_CTX *ctx;
int patchlen;
};
SHA1_Init(&ctx);
memset(&data, 0, sizeof(struct patch_id_t));
data.ctx = &ctx;
- data.xm.consume = patch_id_consume;
for (i = 0; i < q->nr; i++) {
xpparam_t xpp;
xpp.flags = XDF_NEED_MINIMAL;
xecfg.ctxlen = 3;
xecfg.flags = XDL_EMIT_FUNCNAMES;
- ecb.outf = xdiff_outf;
- ecb.priv = &data;
- xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+ xdi_diff_outf(&mf1, &mf2, patch_id_consume, &data,
+ &xpp, &xecfg, &ecb);
}
SHA1_Final(sha1, &ctx);
struct diffstat_t diffstat;
memset(&diffstat, 0, sizeof(struct diffstat_t));
- diffstat.xm.consume = diffstat_consume;
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
if (check_pair_status(p))
$head ||= 'HEAD';
my @refs;
my ($url, $rev, $uuid, $gs) = working_head_info($head, \@refs);
+ unless ($gs) {
+ die "Unable to determine upstream SVN information from ",
+ "$head history.\nPerhaps the repository is empty.";
+ }
$url = defined $_commit_url ? $_commit_url : $gs->full_url;
my $last_rev = $_revision if defined $_revision;
if ($url) {
print "Committing to $url ...\n";
}
- unless ($gs) {
- die "Unable to determine upstream SVN information from ",
- "$head history.\nPerhaps the repository is empty.";
- }
my ($linear_refs, $parents) = linearize_history($gs, \@refs);
if ($_no_rebase && scalar(@$linear_refs) > 1) {
warn "Attempting to commit more than one change while ",
}
}
+sub escape_uri_only {
+ my ($uri) = @_;
+ my @tmp;
+ foreach (split m{/}, $uri) {
+ s/([^\w.%+-]|%(?![a-fA-F0-9]{2}))/sprintf("%%%02X",ord($1))/eg;
+ push @tmp, $_;
+ }
+ join('/', @tmp);
+}
+
+sub escape_url {
+ my ($url) = @_;
+ if ($url =~ m#^([^:]+)://([^/]*)(.*)$#) {
+ my ($scheme, $domain, $uri) = ($1, $2, escape_uri_only($3));
+ $url = "$scheme://$domain$uri";
+ }
+ $url;
+}
+
sub cmd_info {
my $path = canonicalize_path(defined($_[0]) ? $_[0] : ".");
+ my $fullpath = canonicalize_path($cmd_dir_prefix . $path);
if (exists $_[1]) {
die "Too many arguments specified\n";
}
my ($file_type, $diff_status) = find_file_type_and_diff_status($path);
if (!$file_type && !$diff_status) {
- print STDERR "$path: (Not a versioned resource)\n\n";
- return;
+ print STDERR "svn: '$path' is not under version control\n";
+ exit 1;
}
my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
# canonicalize_path() will return "" to make libsvn 1.5.x happy,
$path = "." if $path eq "";
- my $full_url = $url . ($path eq "." ? "" : "/$path");
+ my $full_url = $url . ($fullpath eq "" ? "" : "/$fullpath");
if ($_url) {
- print $full_url, "\n";
+ print escape_url($full_url), "\n";
return;
}
my $result = "Path: $path\n";
$result .= "Name: " . basename($path) . "\n" if $file_type ne "dir";
- $result .= "URL: " . $full_url . "\n";
+ $result .= "URL: " . escape_url($full_url) . "\n";
eval {
my $repos_root = $gs->repos_root;
Git::SVN::remove_username($repos_root);
- $result .= "Repository Root: $repos_root\n";
+ $result .= "Repository Root: " . escape_url($repos_root) . "\n";
};
if ($@) {
$result .= "Repository Root: (offline)\n";
}
my ($lc_author, $lc_rev, $lc_date_utc);
- my @args = Git::SVN::Log::git_svn_log_cmd($rev, $rev, "--", $path);
+ my @args = Git::SVN::Log::git_svn_log_cmd($rev, $rev, "--", $fullpath);
my $log = command_output_pipe(@args);
my $esc_color = qr/(?:\033\[(?:(?:\d+;)*\d*)?m)*/;
while (<$log>) {
my $out = syswrite($tmp_fh, $str, $res);
defined($out) && $out == $res
or croak("write ",
- $tmp_fh->filename,
+ Git::temp_path($tmp_fh),
": $!\n");
}
defined $res or croak $!;
}
$hash = $::_repository->hash_and_insert_object(
- $fh->filename);
+ Git::temp_path($fh));
$hash =~ /^[a-f\d]{40}$/ or die "not a sha1: $hash\n";
Git::temp_release($fb->{base}, 1);
while (<$diff_fh>) {
chomp $_; # this gets rid of the trailing "\0"
if ($state eq 'meta' && /^:(\d{6})\s(\d{6})\s
- $::sha1\s($::sha1)\s
+ ($::sha1)\s($::sha1)\s
([MTCRAD])\d*$/xo) {
push @mods, { mode_a => $1, mode_b => $2,
- sha1_b => $3, chg => $4 };
- if ($4 =~ /^(?:C|R)$/) {
+ sha1_a => $3, sha1_b => $4,
+ chg => $5 };
+ if ($5 =~ /^(?:C|R)$/) {
$state = 'file_a';
} else {
$state = 'file_b';
my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
$self->url_path($m->{file_a}), $self->{r});
print "\tR\t$m->{file_a} => $m->{file_b}\n" unless $::_q;
+ $self->apply_autoprops($file, $fbat);
$self->chg_file($fbat, $m);
$self->close_file($fbat,undef,$self->{pool});
$self->SUPER::change_file_prop($fbat, $pname, $pval, $self->{pool});
}
-sub chg_file {
- my ($self, $fbat, $m) = @_;
- if ($m->{mode_b} =~ /755$/ && $m->{mode_a} !~ /755$/) {
- $self->change_file_prop($fbat,'svn:executable','*');
- } elsif ($m->{mode_b} !~ /755$/ && $m->{mode_a} =~ /755$/) {
- $self->change_file_prop($fbat,'svn:executable',undef);
- }
- my $fh = Git::temp_acquire('git_blob');
- if ($m->{mode_b} =~ /^120/) {
+sub _chg_file_get_blob ($$$$) {
+ my ($self, $fbat, $m, $which) = @_;
+ my $fh = Git::temp_acquire("git_blob_$which");
+ if ($m->{"mode_$which"} =~ /^120/) {
print $fh 'link ' or croak $!;
$self->change_file_prop($fbat,'svn:special','*');
- } elsif ($m->{mode_a} =~ /^120/ && $m->{mode_b} !~ /^120/) {
+ } elsif ($m->{mode_a} =~ /^120/ && $m->{"mode_$which"} !~ /^120/) {
$self->change_file_prop($fbat,'svn:special',undef);
}
- my $size = $::_repository->cat_blob($m->{sha1_b}, $fh);
- croak "Failed to read object $m->{sha1_b}" if ($size < 0);
+ my $blob = $m->{"sha1_$which"};
+ return ($fh,) if ($blob =~ /^0{40}$/);
+ my $size = $::_repository->cat_blob($blob, $fh);
+ croak "Failed to read object $blob" if ($size < 0);
$fh->flush == 0 or croak $!;
seek $fh, 0, 0 or croak $!;
my $exp = ::md5sum($fh);
seek $fh, 0, 0 or croak $!;
+ return ($fh, $exp);
+}
+sub chg_file {
+ my ($self, $fbat, $m) = @_;
+ if ($m->{mode_b} =~ /755$/ && $m->{mode_a} !~ /755$/) {
+ $self->change_file_prop($fbat,'svn:executable','*');
+ } elsif ($m->{mode_b} !~ /755$/ && $m->{mode_a} =~ /755$/) {
+ $self->change_file_prop($fbat,'svn:executable',undef);
+ }
+ my ($fh_a, $exp_a) = _chg_file_get_blob $self, $fbat, $m, 'a';
+ my ($fh_b, $exp_b) = _chg_file_get_blob $self, $fbat, $m, 'b';
my $pool = SVN::Pool->new;
- my $atd = $self->apply_textdelta($fbat, undef, $pool);
- my $got = SVN::TxDelta::send_stream($fh, @$atd, $pool);
- die "Checksum mismatch\nexpected: $exp\ngot: $got\n" if ($got ne $exp);
- Git::temp_release($fh, 1);
+ my $atd = $self->apply_textdelta($fbat, $exp_a, $pool);
+ if (-s $fh_a) {
+ my $txstream = SVN::TxDelta::new ($fh_a, $fh_b, $pool);
+ my $res = SVN::TxDelta::send_txstream($txstream, @$atd, $pool);
+ if (defined $res) {
+ die "Unexpected result from send_txstream: $res\n",
+ "(SVN::Core::VERSION: $SVN::Core::VERSION)\n";
+ }
+ } else {
+ my $got = SVN::TxDelta::send_stream($fh_b, @$atd, $pool);
+ die "Checksum mismatch\nexpected: $exp_b\ngot: $got\n"
+ if ($got ne $exp_b);
+ }
+ Git::temp_release($fh_b, 1);
+ Git::temp_release($fh_a, 1);
$pool->clear;
}
sub run_pager {
return unless -t *STDOUT && defined $pager;
- pipe my $rfd, my $wfd or return;
+ pipe my ($rfd, $wfd) or return;
defined(my $pid = fork) or ::fatal "Can't fork: $!";
if (!$pid) {
open STDOUT, '>&', $wfd or
test refs/heads/delete-me = "$(git symbolic-ref HEAD)" &&
test_must_fail git checkout --track -b track'
+test_expect_success \
+ 'checkout with --track fakes a sensible -b <name>' '
+ git update-ref refs/remotes/origin/koala/bear renamer &&
+ git update-ref refs/new/koala/bear renamer &&
+
+ git checkout --track origin/koala/bear &&
+ test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
+
+ git checkout master && git branch -D koala/bear &&
+
+ git checkout --track refs/remotes/origin/koala/bear &&
+ test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
+
+ git checkout master && git branch -D koala/bear &&
+
+ git checkout --track remotes/origin/koala/bear &&
+ test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
+
+ git checkout master && git branch -D koala/bear &&
+
+ git checkout --track refs/new/koala/bear &&
+ test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)"
+'
+
+test_expect_success \
+ 'checkout with --track, but without -b, fails with too short tracked name' '
+ test_must_fail git checkout --track renamer'
+
+ test_expect_success 'checkout an unmerged path should fail' '
+ rm -f .git/index &&
+ O=$(echo original | git hash-object -w --stdin) &&
+ A=$(echo ourside | git hash-object -w --stdin) &&
+ B=$(echo theirside | git hash-object -w --stdin) &&
+ (
+ echo "100644 $A 0 fild" &&
+ echo "100644 $O 1 file" &&
+ echo "100644 $A 2 file" &&
+ echo "100644 $B 3 file" &&
+ echo "100644 $A 0 filf"
+ ) | git update-index --index-info &&
+ echo "none of the above" >sample &&
+ cat sample >fild &&
+ cat sample >file &&
+ cat sample >filf &&
+ test_must_fail git checkout fild file filf &&
+ test_cmp sample fild &&
+ test_cmp sample filf &&
+ test_cmp sample file
+ '
+
test_done
use Cwd;
use File::Basename;
- use File::Temp;
BEGIN { use_ok('Git') }
# set up
-our $repo_dir = "trash directory";
our $abs_repo_dir = Cwd->cwd;
-die "this must be run by calling the t/t97* shell script(s)\n"
- if basename(Cwd->cwd) ne $repo_dir;
ok(our $r = Git->repository(Directory => "."), "open repository");
# config
# Failure cases for config:
# Save and restore STDERR; we will probably extract this into a
# "dies_ok" method and possibly move the STDERR handling to Git.pm.
- open our $tmpstderr, ">&", STDERR or die "cannot save STDERR"; close STDERR;
+ open our $tmpstderr, ">&STDERR" or die "cannot save STDERR"; close STDERR;
eval { $r->config("test.dupstring") };
ok($@, "config: duplicate entry in scalar context fails");
eval { $r->config_bool("test.boolother") };
# objects and hashes
ok(our $file1hash = $r->command_oneline('rev-parse', "HEAD:file1"), "(get file hash)");
- our $tmpfile = File::Temp->new;
- is($r->cat_blob($file1hash, $tmpfile), 15, "cat_blob: size");
+ my $tmpfile = "file.tmp";
+ open TEMPFILE, "+>$tmpfile" or die "Can't open $tmpfile: $!";
+ is($r->cat_blob($file1hash, \*TEMPFILE), 15, "cat_blob: size");
our $blobcontents;
- { local $/; seek $tmpfile, 0, 0; $blobcontents = <$tmpfile>; }
+ { local $/; seek TEMPFILE, 0, 0; $blobcontents = <TEMPFILE>; }
is($blobcontents, "changed file 1\n", "cat_blob: data");
- seek $tmpfile, 0, 0;
+ close TEMPFILE or die "Failed writing to $tmpfile: $!";
is(Git::hash_object("blob", $tmpfile), $file1hash, "hash_object: roundtrip");
- $tmpfile = File::Temp->new();
- print $tmpfile my $test_text = "test blob, to be inserted\n";
+ open TEMPFILE, ">$tmpfile" or die "Can't open $tmpfile: $!";
+ print TEMPFILE my $test_text = "test blob, to be inserted\n";
+ close TEMPFILE or die "Failed writing to $tmpfile: $!";
like(our $newhash = $r->hash_and_insert_object($tmpfile), qr/[0-9a-fA-F]{40}/,
"hash_and_insert_object: returns hash");
- $tmpfile = File::Temp->new;
- is($r->cat_blob($newhash, $tmpfile), length $test_text, "cat_blob: roundtrip size");
- { local $/; seek $tmpfile, 0, 0; $blobcontents = <$tmpfile>; }
+ open TEMPFILE, "+>$tmpfile" or die "Can't open $tmpfile: $!";
+ is($r->cat_blob($newhash, \*TEMPFILE), length $test_text, "cat_blob: roundtrip size");
+ { local $/; seek TEMPFILE, 0, 0; $blobcontents = <TEMPFILE>; }
is($blobcontents, $test_text, "cat_blob: roundtrip data");
+ close TEMPFILE;
+ unlink $tmpfile;
# paths
is($r->repo_path, "./.git", "repo_path");
#include "cache.h"
#include "xdiff-interface.h"
+#include "strbuf.h"
+
+struct xdiff_emit_state {
+ xdiff_emit_consume_fn consume;
+ void *consume_callback_data;
+ struct strbuf remainder;
+};
static int parse_num(char **cp_p, int *num_p)
{
unsigned long this_size;
ep = memchr(s, '\n', size);
this_size = (ep == NULL) ? size : (ep - s + 1);
- priv->consume(priv, s, this_size);
+ priv->consume(priv->consume_callback_data, s, this_size);
size -= this_size;
s += this_size;
}
}
-int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf)
+static int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf)
{
struct xdiff_emit_state *priv = priv_;
int i;
for (i = 0; i < nbuf; i++) {
if (mb[i].ptr[mb[i].size-1] != '\n') {
/* Incomplete line */
- priv->remainder = xrealloc(priv->remainder,
- priv->remainder_size +
- mb[i].size);
- memcpy(priv->remainder + priv->remainder_size,
- mb[i].ptr, mb[i].size);
- priv->remainder_size += mb[i].size;
+ strbuf_add(&priv->remainder, mb[i].ptr, mb[i].size);
continue;
}
/* we have a complete line */
- if (!priv->remainder) {
+ if (!priv->remainder.len) {
consume_one(priv, mb[i].ptr, mb[i].size);
continue;
}
- priv->remainder = xrealloc(priv->remainder,
- priv->remainder_size +
- mb[i].size);
- memcpy(priv->remainder + priv->remainder_size,
- mb[i].ptr, mb[i].size);
- consume_one(priv, priv->remainder,
- priv->remainder_size + mb[i].size);
- free(priv->remainder);
- priv->remainder = NULL;
- priv->remainder_size = 0;
+ strbuf_add(&priv->remainder, mb[i].ptr, mb[i].size);
+ consume_one(priv, priv->remainder.buf, priv->remainder.len);
+ strbuf_reset(&priv->remainder);
}
- if (priv->remainder) {
- consume_one(priv, priv->remainder, priv->remainder_size);
- free(priv->remainder);
- priv->remainder = NULL;
- priv->remainder_size = 0;
+ if (priv->remainder.len) {
+ consume_one(priv, priv->remainder.buf, priv->remainder.len);
+ strbuf_reset(&priv->remainder);
}
return 0;
}
return xdl_diff(&a, &b, xpp, xecfg, xecb);
}
+int xdi_diff_outf(mmfile_t *mf1, mmfile_t *mf2,
+ xdiff_emit_consume_fn fn, void *consume_callback_data,
+ xpparam_t const *xpp,
+ xdemitconf_t const *xecfg, xdemitcb_t *xecb)
+{
+ int ret;
+ struct xdiff_emit_state state;
+
+ memset(&state, 0, sizeof(state));
+ state.consume = fn;
+ state.consume_callback_data = consume_callback_data;
+ xecb->outf = xdiff_outf;
+ xecb->priv = &state;
+ strbuf_init(&state.remainder, 0);
+ ret = xdi_diff(mf1, mf2, xpp, xecfg, xecb);
+ strbuf_release(&state.remainder);
+ return ret;
+}
+
int read_mmfile(mmfile_t *ptr, const char *filename)
{
struct stat st;
return result;
}
- void xdiff_set_find_func(xdemitconf_t *xecfg, const char *value)
+ void xdiff_set_find_func(xdemitconf_t *xecfg, const char *value, int cflags)
{
int i;
struct ff_regs *regs;
expression = buffer = xstrndup(value, ep - value);
else
expression = value;
- if (regcomp(®->re, expression, 0))
+ if (regcomp(®->re, expression, cflags))
die("Invalid regexp to look for hunk header: %s", expression);
free(buffer);
value = ep + 1;
#include "xdiff/xdiff.h"
-struct xdiff_emit_state;
-
typedef void (*xdiff_emit_consume_fn)(void *, char *, unsigned long);
-struct xdiff_emit_state {
- xdiff_emit_consume_fn consume;
- char *remainder;
- unsigned long remainder_size;
-};
-
int xdi_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdemitconf_t const *xecfg, xdemitcb_t *ecb);
-int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf);
+int xdi_diff_outf(mmfile_t *mf1, mmfile_t *mf2,
+ xdiff_emit_consume_fn fn, void *consume_callback_data,
+ xpparam_t const *xpp,
+ xdemitconf_t const *xecfg, xdemitcb_t *xecb);
int parse_hunk_header(char *line, int len,
int *ob, int *on,
int *nb, int *nn);
int read_mmfile(mmfile_t *ptr, const char *filename);
int buffer_is_binary(const char *ptr, unsigned long size);
- extern void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line);
+ extern void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line, int cflags);
#endif