aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile17
-rw-r--r--builtin-help.c241
-rw-r--r--builtin-log.c69
-rw-r--r--builtin.h23
-rw-r--r--combine-diff.c89
-rw-r--r--commit-tree.c2
-rw-r--r--commit.h2
-rw-r--r--contrib/colordiff/README2
-rwxr-xr-xcontrib/colordiff/colordiff.perl196
-rw-r--r--diff-files.c215
-rw-r--r--diff-index.c252
-rw-r--r--diff-lib.c344
-rw-r--r--diff-tree.c91
-rw-r--r--diff.c80
-rw-r--r--diff.h17
-rwxr-xr-xgit-repack.sh8
-rw-r--r--git.c369
-rwxr-xr-xgitk20
-rw-r--r--http-push.c1
-rw-r--r--log-tree.c211
-rw-r--r--log-tree.h25
-rw-r--r--pager.c2
-rw-r--r--rev-list.c92
-rw-r--r--revision.c175
-rw-r--r--revision.h23
-rw-r--r--sha1_name.c16
-rwxr-xr-xt/t0000-basic.sh21
-rwxr-xr-xt/t1001-read-tree-m-2way.sh2
-rwxr-xr-xt/t1002-read-tree-m-u-2way.sh2
-rwxr-xr-xt/t1200-tutorial.sh4
-rwxr-xr-xt/t4010-diff-pathspec.sh10
-rw-r--r--tree-walk.c50
-rw-r--r--tree-walk.h2
-rw-r--r--update-index.c150
34 files changed, 1669 insertions, 1154 deletions
diff --git a/Makefile b/Makefile
index 8aed3af01..8ce27a65f 100644
--- a/Makefile
+++ b/Makefile
@@ -199,7 +199,7 @@ LIB_H = \
tree-walk.h log-tree.h
DIFF_OBJS = \
- diff.o diffcore-break.o diffcore-order.o \
+ diff.o diff-lib.o diffcore-break.o diffcore-order.o \
diffcore-pickaxe.o diffcore-rename.o tree-diff.o combine-diff.o \
diffcore-delta.o log-tree.o
@@ -213,6 +213,9 @@ LIB_OBJS = \
fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \
$(DIFF_OBJS)
+BUILTIN_OBJS = \
+ builtin-log.o builtin-help.o
+
GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
LIBS = $(GITLIBS) -lz
@@ -462,10 +465,12 @@ all:
strip: $(PROGRAMS) git$X
$(STRIP) $(STRIP_OPTS) $(PROGRAMS) git$X
-git$X: git.c common-cmds.h $(GITLIBS)
+git$X: git.c common-cmds.h $(BUILTIN_OBJS) $(GITLIBS)
$(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \
$(ALL_CFLAGS) -o $@ $(filter %.c,$^) \
- $(ALL_LDFLAGS) $(LIBS)
+ $(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)
+
+builtin-help.o: common-cmds.h
$(BUILT_INS): git$X
rm -f $@ && ln git$X $@
@@ -565,17 +570,17 @@ init-db.o: init-db.c
$(CC) -c $(ALL_CFLAGS) \
-DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"' $*.c
-$(LIB_OBJS): $(LIB_H)
+$(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
$(patsubst git-%$X,%.o,$(PROGRAMS)): $(GITLIBS)
$(DIFF_OBJS): diffcore.h
$(LIB_FILE): $(LIB_OBJS)
- $(AR) rcs $@ $(LIB_OBJS)
+ rm -f $@ && $(AR) rcs $@ $(LIB_OBJS)
XDIFF_OBJS=xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o
$(XDIFF_LIB): $(XDIFF_OBJS)
- $(AR) rcs $@ $(XDIFF_OBJS)
+ rm -f $@ && $(AR) rcs $@ $(XDIFF_OBJS)
doc:
diff --git a/builtin-help.c b/builtin-help.c
new file mode 100644
index 000000000..10a59cc40
--- /dev/null
+++ b/builtin-help.c
@@ -0,0 +1,241 @@
+/*
+ * builtin-help.c
+ *
+ * Builtin help-related commands (help, usage, version)
+ */
+#include "cache.h"
+#include "builtin.h"
+#include "exec_cmd.h"
+#include "common-cmds.h"
+
+static const char git_usage[] =
+ "Usage: git [--version] [--exec-path[=GIT_EXEC_PATH]] [--help] COMMAND [ ARGS ]";
+
+/* most gui terms set COLUMNS (although some don't export it) */
+static int term_columns(void)
+{
+ char *col_string = getenv("COLUMNS");
+ int n_cols = 0;
+
+ if (col_string && (n_cols = atoi(col_string)) > 0)
+ return n_cols;
+
+#ifdef TIOCGWINSZ
+ {
+ struct winsize ws;
+ if (!ioctl(1, TIOCGWINSZ, &ws)) {
+ if (ws.ws_col)
+ return ws.ws_col;
+ }
+ }
+#endif
+
+ return 80;
+}
+
+static void oom(void)
+{
+ fprintf(stderr, "git: out of memory\n");
+ exit(1);
+}
+
+static inline void mput_char(char c, unsigned int num)
+{
+ while(num--)
+ putchar(c);
+}
+
+static struct cmdname {
+ size_t len;
+ char name[1];
+} **cmdname;
+static int cmdname_alloc, cmdname_cnt;
+
+static void add_cmdname(const char *name, int len)
+{
+ struct cmdname *ent;
+ if (cmdname_alloc <= cmdname_cnt) {
+ cmdname_alloc = cmdname_alloc + 200;
+ cmdname = realloc(cmdname, cmdname_alloc * sizeof(*cmdname));
+ if (!cmdname)
+ oom();
+ }
+ ent = malloc(sizeof(*ent) + len);
+ if (!ent)
+ oom();
+ ent->len = len;
+ memcpy(ent->name, name, len);
+ ent->name[len] = 0;
+ cmdname[cmdname_cnt++] = ent;
+}
+
+static int cmdname_compare(const void *a_, const void *b_)
+{
+ struct cmdname *a = *(struct cmdname **)a_;
+ struct cmdname *b = *(struct cmdname **)b_;
+ return strcmp(a->name, b->name);
+}
+
+static void pretty_print_string_list(struct cmdname **cmdname, int longest)
+{
+ int cols = 1, rows;
+ int space = longest + 1; /* min 1 SP between words */
+ int max_cols = term_columns() - 1; /* don't print *on* the edge */
+ int i, j;
+
+ if (space < max_cols)
+ cols = max_cols / space;
+ rows = (cmdname_cnt + cols - 1) / cols;
+
+ qsort(cmdname, cmdname_cnt, sizeof(*cmdname), cmdname_compare);
+
+ for (i = 0; i < rows; i++) {
+ printf(" ");
+
+ for (j = 0; j < cols; j++) {
+ int n = j * rows + i;
+ int size = space;
+ if (n >= cmdname_cnt)
+ break;
+ if (j == cols-1 || n + rows >= cmdname_cnt)
+ size = 1;
+ printf("%-*s", size, cmdname[n]->name);
+ }
+ putchar('\n');
+ }
+}
+
+static void list_commands(const char *exec_path, const char *pattern)
+{
+ unsigned int longest = 0;
+ char path[PATH_MAX];
+ int dirlen;
+ DIR *dir = opendir(exec_path);
+ struct dirent *de;
+
+ if (!dir) {
+ fprintf(stderr, "git: '%s': %s\n", exec_path, strerror(errno));
+ exit(1);
+ }
+
+ dirlen = strlen(exec_path);
+ if (PATH_MAX - 20 < dirlen) {
+ fprintf(stderr, "git: insanely long exec-path '%s'\n",
+ exec_path);
+ exit(1);
+ }
+
+ memcpy(path, exec_path, dirlen);
+ path[dirlen++] = '/';
+
+ while ((de = readdir(dir)) != NULL) {
+ struct stat st;
+ int entlen;
+
+ if (strncmp(de->d_name, "git-", 4))
+ continue;
+ strcpy(path+dirlen, de->d_name);
+ if (stat(path, &st) || /* stat, not lstat */
+ !S_ISREG(st.st_mode) ||
+ !(st.st_mode & S_IXUSR))
+ continue;
+
+ entlen = strlen(de->d_name);
+ if (4 < entlen && !strcmp(de->d_name + entlen - 4, ".exe"))
+ entlen -= 4;
+
+ if (longest < entlen)
+ longest = entlen;
+
+ add_cmdname(de->d_name + 4, entlen-4);
+ }
+ closedir(dir);
+
+ printf("git commands available in '%s'\n", exec_path);
+ printf("----------------------------");
+ mput_char('-', strlen(exec_path));
+ putchar('\n');
+ pretty_print_string_list(cmdname, longest - 4);
+ putchar('\n');
+}
+
+static void list_common_cmds_help(void)
+{
+ int i, longest = 0;
+
+ for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
+ if (longest < strlen(common_cmds[i].name))
+ longest = strlen(common_cmds[i].name);
+ }
+
+ puts("The most commonly used git commands are:");
+ for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
+ printf(" %s", common_cmds[i].name);
+ mput_char(' ', longest - strlen(common_cmds[i].name) + 4);
+ puts(common_cmds[i].help);
+ }
+ puts("(use 'git help -a' to get a list of all installed git commands)");
+}
+
+void cmd_usage(int show_all, const char *exec_path, const char *fmt, ...)
+{
+ if (fmt) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ printf("git: ");
+ vprintf(fmt, ap);
+ va_end(ap);
+ putchar('\n');
+ }
+ else
+ puts(git_usage);
+
+ if (exec_path) {
+ putchar('\n');
+ if (show_all)
+ list_commands(exec_path, "git-*");
+ else
+ list_common_cmds_help();
+ }
+
+ exit(1);
+}
+
+static void show_man_page(const char *git_cmd)
+{
+ const char *page;
+
+ if (!strncmp(git_cmd, "git", 3))
+ page = git_cmd;
+ else {
+ int page_len = strlen(git_cmd) + 4;
+ char *p = malloc(page_len + 1);
+ strcpy(p, "git-");
+ strcpy(p + 4, git_cmd);
+ p[page_len] = 0;
+ page = p;
+ }
+
+ execlp("man", "man", page, NULL);
+}
+
+int cmd_version(int argc, const char **argv, char **envp)
+{
+ printf("git version %s\n", git_version_string);
+ return 0;
+}
+
+int cmd_help(int argc, const char **argv, char **envp)
+{
+ const char *help_cmd = argv[1];
+ if (!help_cmd)
+ cmd_usage(0, git_exec_path(), NULL);
+ else if (!strcmp(help_cmd, "--all") || !strcmp(help_cmd, "-a"))
+ cmd_usage(1, git_exec_path(), NULL);
+ else
+ show_man_page(help_cmd);
+ return 0;
+}
+
+
diff --git a/builtin-log.c b/builtin-log.c
new file mode 100644
index 000000000..69f2911cb
--- /dev/null
+++ b/builtin-log.c
@@ -0,0 +1,69 @@
+/*
+ * Builtin "git log" and related commands (show, whatchanged)
+ *
+ * (C) Copyright 2006 Linus Torvalds
+ * 2006 Junio Hamano
+ */
+#include "cache.h"
+#include "commit.h"
+#include "diff.h"
+#include "revision.h"
+#include "log-tree.h"
+
+static int cmd_log_wc(int argc, const char **argv, char **envp,
+ struct rev_info *rev)
+{
+ struct commit *commit;
+
+ rev->abbrev = DEFAULT_ABBREV;
+ rev->commit_format = CMIT_FMT_DEFAULT;
+ rev->verbose_header = 1;
+ argc = setup_revisions(argc, argv, rev, "HEAD");
+
+ if (argc > 1)
+ die("unrecognized argument: %s", argv[1]);
+
+ prepare_revision_walk(rev);
+ setup_pager();
+ while ((commit = get_revision(rev)) != NULL) {
+ log_tree_commit(rev, commit);
+ free(commit->buffer);
+ commit->buffer = NULL;
+ }
+ return 0;
+}
+
+int cmd_whatchanged(int argc, const char **argv, char **envp)
+{
+ struct rev_info rev;
+
+ init_revisions(&rev);
+ rev.diff = 1;
+ rev.diffopt.recursive = 1;
+ return cmd_log_wc(argc, argv, envp, &rev);
+}
+
+int cmd_show(int argc, const char **argv, char **envp)
+{
+ struct rev_info rev;
+
+ init_revisions(&rev);
+ rev.diff = 1;
+ rev.diffopt.recursive = 1;
+ rev.combine_merges = 1;
+ rev.dense_combined_merges = 1;
+ rev.always_show_header = 1;
+ rev.ignore_merges = 0;
+ rev.no_walk = 1;
+ return cmd_log_wc(argc, argv, envp, &rev);
+}
+
+int cmd_log(int argc, const char **argv, char **envp)
+{
+ struct rev_info rev;
+
+ init_revisions(&rev);
+ rev.always_show_header = 1;
+ rev.diffopt.recursive = 1;
+ return cmd_log_wc(argc, argv, envp, &rev);
+}
diff --git a/builtin.h b/builtin.h
new file mode 100644
index 000000000..47408a058
--- /dev/null
+++ b/builtin.h
@@ -0,0 +1,23 @@
+#ifndef BUILTIN_H
+#define BUILTIN_H
+
+#ifndef PATH_MAX
+# define PATH_MAX 4096
+#endif
+
+extern const char git_version_string[];
+
+void cmd_usage(int show_all, const char *exec_path, const char *fmt, ...)
+#ifdef __GNUC__
+ __attribute__((__format__(__printf__, 3, 4), __noreturn__))
+#endif
+ ;
+
+extern int cmd_help(int argc, const char **argv, char **envp);
+extern int cmd_version(int argc, const char **argv, char **envp);
+
+extern int cmd_whatchanged(int argc, const char **argv, char **envp);
+extern int cmd_show(int argc, const char **argv, char **envp);
+extern int cmd_log(int argc, const char **argv, char **envp);
+
+#endif
diff --git a/combine-diff.c b/combine-diff.c
index 9445e86c2..ca36f5d5e 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -5,6 +5,7 @@
#include "diffcore.h"
#include "quote.h"
#include "xdiff-interface.h"
+#include "log-tree.h"
static int uninteresting(struct diff_filepair *p)
{
@@ -584,10 +585,20 @@ static void reuse_combine_diff(struct sline *sline, unsigned long cnt,
sline->p_lno[i] = sline->p_lno[j];
}
+static void dump_quoted_path(const char *prefix, const char *path)
+{
+ fputs(prefix, stdout);
+ if (quote_c_style(path, NULL, NULL, 0))
+ quote_c_style(path, NULL, stdout, 0);
+ else
+ printf("%s", path);
+ putchar('\n');
+}
+
static int show_patch_diff(struct combine_diff_path *elem, int num_parent,
- int dense, const char *header,
- struct diff_options *opt)
+ int dense, struct rev_info *rev)
{
+ struct diff_options *opt = &rev->diffopt;
unsigned long result_size, cnt, lno;
char *result, *cp;
struct sline *sline; /* survived lines */
@@ -688,16 +699,9 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent,
if (show_hunks || mode_differs || working_tree_file) {
const char *abb;
- if (header) {
- shown_header++;
- printf("%s%c", header, opt->line_termination);
- }
- printf("diff --%s ", dense ? "cc" : "combined");
- if (quote_c_style(elem->path, NULL, NULL, 0))
- quote_c_style(elem->path, NULL, stdout, 0);
- else
- printf("%s", elem->path);
- putchar('\n');
+ if (rev->loginfo)
+ show_log(rev, rev->loginfo, "\n");
+ dump_quoted_path(dense ? "diff --cc " : "diff --combined ", elem->path);
printf("index ");
for (i = 0; i < num_parent; i++) {
abb = find_unique_abbrev(elem->parent[i].sha1,
@@ -728,6 +732,8 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent,
}
putchar('\n');
}
+ dump_quoted_path("--- a/", elem->path);
+ dump_quoted_path("+++ b/", elem->path);
dump_sline(sline, cnt, num_parent);
}
free(result);
@@ -749,8 +755,9 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent,
#define COLONS "::::::::::::::::::::::::::::::::"
-static void show_raw_diff(struct combine_diff_path *p, int num_parent, const char *header, struct diff_options *opt)
+static void show_raw_diff(struct combine_diff_path *p, int num_parent, struct rev_info *rev)
{
+ struct diff_options *opt = &rev->diffopt;
int i, offset;
const char *prefix;
int line_termination, inter_name_termination;
@@ -760,8 +767,8 @@ static void show_raw_diff(struct combine_diff_path *p, int num_parent, const cha
if (!line_termination)
inter_name_termination = 0;
- if (header)
- printf("%s%c", header, line_termination);
+ if (rev->loginfo)
+ show_log(rev, rev->loginfo, "\n");
if (opt->output_format == DIFF_FORMAT_RAW) {
offset = strlen(COLONS) - num_parent;
@@ -802,40 +809,44 @@ static void show_raw_diff(struct combine_diff_path *p, int num_parent, const cha
}
}
-int show_combined_diff(struct combine_diff_path *p,
+void show_combined_diff(struct combine_diff_path *p,
int num_parent,
int dense,
- const char *header,
- struct diff_options *opt)
+ struct rev_info *rev)
{
+ struct diff_options *opt = &rev->diffopt;
if (!p->len)
- return 0;
+ return;
switch (opt->output_format) {
case DIFF_FORMAT_RAW:
case DIFF_FORMAT_NAME_STATUS:
case DIFF_FORMAT_NAME:
- show_raw_diff(p, num_parent, header, opt);
- return 1;
-
- default:
+ show_raw_diff(p, num_parent, rev);
+ return;
case DIFF_FORMAT_PATCH:
- return show_patch_diff(p, num_parent, dense, header, opt);
+ show_patch_diff(p, num_parent, dense, rev);
+ return;
+ default:
+ return;
}
}
-const char *diff_tree_combined_merge(const unsigned char *sha1,
- const char *header, int dense,
- struct diff_options *opt)
+void diff_tree_combined_merge(const unsigned char *sha1,
+ int dense, struct rev_info *rev)
{
+ struct diff_options *opt = &rev->diffopt;
struct commit *commit = lookup_commit(sha1);
struct diff_options diffopts;
struct commit_list *parents;
struct combine_diff_path *p, *paths = NULL;
int num_parent, i, num_paths;
+ int do_diffstat;
+ do_diffstat = (opt->output_format == DIFF_FORMAT_DIFFSTAT ||
+ opt->with_stat);
diffopts = *opt;
- diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
diffopts.with_raw = 0;
+ diffopts.with_stat = 0;
diffopts.recursive = 1;
/* count parents */
@@ -849,11 +860,24 @@ const char *diff_tree_combined_merge(const unsigned char *sha1,
parents;
parents = parents->next, i++) {
struct commit *parent = parents->item;
+ /* show stat against the first parent even
+ * when doing combined diff.
+ */
+ if (i == 0 && do_diffstat)
+ diffopts.output_format = DIFF_FORMAT_DIFFSTAT;
+ else
+ diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
diff_tree_sha1(parent->object.sha1, commit->object.sha1, "",
&diffopts);
diffcore_std(&diffopts);
paths = intersect_paths(paths, i, num_parent);
+
+ if (do_diffstat && rev->loginfo)
+ show_log(rev, rev->loginfo,
+ opt->with_stat ? "---\n" : "\n");
diff_flush(&diffopts);
+ if (opt->with_stat)
+ putchar('\n');
}
/* find out surviving paths */
@@ -866,17 +890,13 @@ const char *diff_tree_combined_merge(const unsigned char *sha1,
int saved_format = opt->output_format;
opt->output_format = DIFF_FORMAT_RAW;
for (p = paths; p; p = p->next) {
- if (show_combined_diff(p, num_parent, dense,
- header, opt))
- header = NULL;
+ show_combined_diff(p, num_parent, dense, rev);
}
opt->output_format = saved_format;
putchar(opt->line_termination);
}
for (p = paths; p; p = p->next) {
- if (show_combined_diff(p, num_parent, dense,
- header, opt))
- header = NULL;
+ show_combined_diff(p, num_parent, dense, rev);
}
}
@@ -886,5 +906,4 @@ const char *diff_tree_combined_merge(const unsigned char *sha1,
paths = paths->next;
free(tmp);
}
- return header;
}
diff --git a/commit-tree.c b/commit-tree.c
index 259585097..bad72e89e 100644
--- a/commit-tree.c
+++ b/commit-tree.c
@@ -91,7 +91,7 @@ int main(int argc, char **argv)
git_config(git_default_config);
- if (argc < 2 || get_sha1_hex(argv[1], tree_sha1) < 0)
+ if (argc < 2 || get_sha1(argv[1], tree_sha1) < 0)
usage(commit_tree_usage);
check_valid(tree_sha1, tree_type);
diff --git a/commit.h b/commit.h
index 918c9ab5e..de142afe7 100644
--- a/commit.h
+++ b/commit.h
@@ -45,6 +45,8 @@ enum cmit_fmt {
CMIT_FMT_FULL,
CMIT_FMT_FULLER,
CMIT_FMT_ONELINE,
+
+ CMIT_FMT_UNSPECIFIED,
};
extern enum cmit_fmt get_commit_format(const char *arg);
diff --git a/contrib/colordiff/README b/contrib/colordiff/README
new file mode 100644
index 000000000..2678fdf9c
--- /dev/null
+++ b/contrib/colordiff/README
@@ -0,0 +1,2 @@
+This is "colordiff" (http://colordiff.sourceforge.net/) by Dave
+Ewart <davee@sungate.co.uk>, modified specifically for git.
diff --git a/contrib/colordiff/colordiff.perl b/contrib/colordiff/colordiff.perl
new file mode 100755
index 000000000..5789cfb26
--- /dev/null
+++ b/contrib/colordiff/colordiff.perl
@@ -0,0 +1,196 @@
+#!/usr/bin/perl -w
+#
+# $Id: colordiff.pl,v 1.4.2.10 2004/01/04 15:02:59 daveewart Exp $
+
+########################################################################
+# #
+# ColorDiff - a wrapper/replacment for 'diff' producing #
+# colourful output #
+# #
+# Copyright (C)2002-2004 Dave Ewart (davee@sungate.co.uk) #
+# #
+########################################################################
+# #
+# This program is free software; you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation; either version 2 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program; if not, write to the Free Software #
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #
+# #
+########################################################################
+
+use strict;
+use Getopt::Long qw(:config pass_through);
+use IPC::Open2;
+
+my $app_name = 'colordiff';
+my $version = '1.0.4';
+my $author = 'Dave Ewart';
+my $author_email = 'davee@sungate.co.uk';
+my $app_www = 'http://colordiff.sourceforge.net/';
+my $copyright = '(C)2002-2004';
+my $show_banner = 1;
+
+# ANSI sequences for colours
+my %colour;
+$colour{white} = "\033[1;37m";
+$colour{yellow} = "\033[1;33m";
+$colour{green} = "\033[1;32m";
+$colour{blue} = "\033[1;34m";
+$colour{cyan} = "\033[1;36m";
+$colour{red} = "\033[1;31m";
+$colour{magenta} = "\033[1;35m";
+$colour{black} = "\033[1;30m";
+$colour{darkwhite} = "\033[0;37m";
+$colour{darkyellow} = "\033[0;33m";
+$colour{darkgreen} = "\033[0;32m";
+$colour{darkblue} = "\033[0;34m";
+$colour{darkcyan} = "\033[0;36m";
+$colour{darkred} = "\033[0;31m";
+$colour{darkmagenta} = "\033[0;35m";
+$colour{darkblack} = "\033[0;30m";
+$colour{OFF} = "\033[0;0m";
+
+# Default colours if /etc/colordiffrc or ~/.colordiffrc do not exist
+my $plain_text = $colour{OFF};
+my $file_old = $colour{red};
+my $file_new = $colour{blue};
+my $diff_stuff = $colour{magenta};
+
+# Locations for personal and system-wide colour configurations
+my $HOME = $ENV{HOME};
+my $etcdir = '/etc';
+
+my ($setting, $value);
+my @config_files = ("$etcdir/colordiffrc", "$HOME/.colordiffrc");
+my $config_file;
+
+foreach $config_file (@config_files) {
+ if (open(COLORDIFFRC, "<$config_file")) {
+ while (<COLORDIFFRC>) {
+ chop;
+ next if (/^#/ || /^$/);
+ s/\s+//g;
+ ($setting, $value) = split ('=');
+ if ($setting eq 'banner') {
+ if ($value eq 'no') {
+ $show_banner = 0;
+ }
+ next;
+ }
+ if (!defined $colour{$value}) {
+ print "Invalid colour specification ($value) in $config_file\n";
+ next;
+ }
+ if ($setting eq 'plain') {
+ $plain_text = $colour{$value};
+ }
+ elsif ($setting eq 'oldtext') {
+ $file_old = $colour{$value};
+ }
+ elsif ($setting eq 'newtext') {
+ $file_new = $colour{$value};
+ }
+ elsif ($setting eq 'diffstuff') {
+ $diff_stuff = $colour{$value};
+ }
+ else {
+ print "Unknown option in $etcdir/colordiffrc: $setting\n";
+ }
+ }
+ close COLORDIFFRC;
+ }
+}
+
+# colordiff specfic options here. Need to pre-declare if using variables
+GetOptions(
+ "no-banner" => sub { $show_banner = 0 },
+ "plain-text=s" => \&set_color,
+ "file-old=s" => \&set_color,
+ "file-new=s" => \&set_color,
+ "diff-stuff=s" => \&set_color
+);
+
+if ($show_banner == 1) {
+ print STDERR "$app_name $version ($app_www)\n";
+ print STDERR "$copyright $author, $author_email\n\n";
+}
+
+if (defined $ARGV[0]) {
+ # More reliable way of pulling in arguments
+ open2(\*INPUTSTREAM, undef, "git", "diff", @ARGV);
+}
+else {
+ *INPUTSTREAM = \*STDIN;
+}
+
+my $record;
+my $nrecs = 0;
+my $inside_file_old = 1;
+my $nparents = undef;
+
+while (<INPUTSTREAM>) {
+ $nrecs++;
+ if (/^(\@\@+) -[-+0-9, ]+ \1/) {
+ print "$diff_stuff";
+ $nparents = length($1) - 1;
+ }
+ elsif (/^diff -/ || /^index / ||
+ /^old mode / || /^new mode / ||
+ /^deleted file mode / || /^new file mode / ||
+ /^similarity index / || /^dissimilarity index / ||
+ /^copy from / || /^copy to / ||
+ /^rename from / || /^rename to /) {
+ $nparents = undef;
+ print "$diff_stuff";
+ }
+ elsif (defined $nparents) {
+ if ($nparents == 1) {
+ if (/^\+/) {
+ print $file_new;
+ }
+ elsif (/^-/) {
+ print $file_old;
+ }
+ else {
+ print $plain_text;
+ }
+ }
+ elsif (/^ {$nparents}/) {
+ print "$plain_text";
+ }
+ elsif (/^[+ ]{$nparents}/) {
+ print "$file_new";
+ }
+ elsif (/^[- ]{$nparents}/) {
+ print "$file_old";
+ }
+ else {
+ print $plain_text;
+ }
+ }
+ elsif (/^--- / || /^\+\+\+ /) {
+ print $diff_stuff;
+ }
+ else {
+ print "$plain_text";
+ }
+ s/$/$colour{OFF}/;
+ print "$_";
+}
+close INPUTSTREAM;
+
+sub set_color {
+ my ($type, $color) = @_;
+
+ $type =~ s/-/_/;
+ eval "\$$type = \$colour{$color}";
+}
diff --git a/diff-files.c b/diff-files.c
index 3e7f5f105..b9d193d50 100644
--- a/diff-files.c
+++ b/diff-files.c
@@ -5,209 +5,50 @@
*/
#include "cache.h"
#include "diff.h"
+#include "commit.h"
+#include "revision.h"
static const char diff_files_usage[] =
"git-diff-files [-q] [-0/-1/2/3 |-c|--cc] [<common diff options>] [<path>...]"
COMMON_DIFF_OPTIONS_HELP;
-static struct diff_options diff_options;
-static int silent = 0;
-static int diff_unmerged_stage = 2;
-static int combine_merges = 0;
-static int dense_combined_merges = 0;
-
-static void show_unmerge(const char *path)
-{
- diff_unmerge(&diff_options, path);
-}
-
-static void show_file(int pfx, struct cache_entry *ce)
-{
- diff_addremove(&diff_options, pfx, ntohl(ce->ce_mode),
- ce->sha1, ce->name, NULL);
-}
-
-static void show_modified(int oldmode, int mode,
- const unsigned char *old_sha1, const unsigned char *sha1,
- char *path)
-{
- diff_change(&diff_options, oldmode, mode, old_sha1, sha1, path, NULL);
-}
-
int main(int argc, const char **argv)
{
- const char **pathspec;
- const char *prefix = setup_git_directory();
- int entries, i;
+ struct rev_info rev;
+ int silent = 0;
git_config(git_diff_config);
- diff_setup(&diff_options);
+ init_revisions(&rev);
+ rev.abbrev = 0;
+
+ argc = setup_revisions(argc, argv, &rev, NULL);
while (1 < argc && argv[1][0] == '-') {
- if (!strcmp(argv[1], "--")) {
- argv++;
- argc--;
- break;
- }
- if (!strcmp(argv[1], "-0"))
- diff_unmerged_stage = 0;
- else if (!strcmp(argv[1], "-1"))
- diff_unmerged_stage = 1;
- else if (!strcmp(argv[1], "-2"))
- diff_unmerged_stage = 2;
- else if (!strcmp(argv[1], "-3"))
- diff_unmerged_stage = 3;
- else if (!strcmp(argv[1], "--base"))
- diff_unmerged_stage = 1;
+ if (!strcmp(argv[1], "--base"))
+ rev.max_count = 1;
else if (!strcmp(argv[1], "--ours"))
- diff_unmerged_stage = 2;
+ rev.max_count = 2;
else if (!strcmp(argv[1], "--theirs"))
- diff_unmerged_stage = 3;
+ rev.max_count = 3;
else if (!strcmp(argv[1], "-q"))
silent = 1;
- else if (!strcmp(argv[1], "-r"))
- ; /* no-op */
- else if (!strcmp(argv[1], "-s"))
- ; /* no-op */
- else if (!strcmp(argv[1], "-c"))
- combine_merges = 1;
- else if (!strcmp(argv[1], "--cc"))
- dense_combined_merges = combine_merges = 1;
- else {
- int diff_opt_cnt;
- diff_opt_cnt = diff_opt_parse(&diff_options,
- argv+1, argc-1);
- if (diff_opt_cnt < 0)
- usage(diff_files_usage);
- else if (diff_opt_cnt) {
- argv += diff_opt_cnt;
- argc -= diff_opt_cnt;
- continue;
- }
- else
- usage(diff_files_usage);
- }
+ else
+ usage(diff_files_usage);
argv++; argc--;
}
- if (dense_combined_merges)
- diff_options.output_format = DIFF_FORMAT_PATCH;
-
- /* Find the directory, and set up the pathspec */
- pathspec = get_pathspec(prefix, argv + 1);
- entries = read_cache();
-
- if (diff_setup_done(&diff_options) < 0)
+ /*
+ * Make sure there are NO revision (i.e. pending object) parameter,
+ * rev.max_count is reasonable (0 <= n <= 3),
+ * there is no other revision filtering parameters.
+ */
+ if (rev.pending_objects ||
+ rev.min_age != -1 || rev.max_age != -1)
usage(diff_files_usage);
-
- /* At this point, if argc == 1, then we are doing everything.
- * Otherwise argv[1] .. argv[argc-1] have the explicit paths.
+ /*
+ * Backward compatibility wart - "diff-files -s" used to
+ * defeat the common diff option "-s" which asked for
+ * DIFF_FORMAT_NO_OUTPUT.
*/
- if (entries < 0) {
- perror("read_cache");
- exit(1);
- }
-
- for (i = 0; i < entries; i++) {
- struct stat st;
- unsigned int oldmode, newmode;
- struct cache_entry *ce = active_cache[i];
- int changed;
-
- if (!ce_path_match(ce, pathspec))
- continue;
-
- if (ce_stage(ce)) {
- struct {
- struct combine_diff_path p;
- struct combine_diff_parent filler[5];
- } combine;
- int num_compare_stages = 0;
-
- combine.p.next = NULL;
- combine.p.len = ce_namelen(ce);
- combine.p.path = xmalloc(combine.p.len + 1);
- memcpy(combine.p.path, ce->name, combine.p.len);
- combine.p.path[combine.p.len] = 0;
- combine.p.mode = 0;
- memset(combine.p.sha1, 0, 20);
- memset(&combine.p.parent[0], 0,
- sizeof(combine.filler));
-
- while (i < entries) {
- struct cache_entry *nce = active_cache[i];
- int stage;
-
- if (strcmp(ce->name, nce->name))
- break;
-
- /* Stage #2 (ours) is the first parent,
- * stage #3 (theirs) is the second.
- */
- stage = ce_stage(nce);
- if (2 <= stage) {
- int mode = ntohl(nce->ce_mode);
- num_compare_stages++;
- memcpy(combine.p.parent[stage-2].sha1,
- nce->sha1, 20);
- combine.p.parent[stage-2].mode =
- canon_mode(mode);
- combine.p.parent[stage-2].status =
- DIFF_STATUS_MODIFIED;
- }
-
- /* diff against the proper unmerged stage */
- if (stage == diff_unmerged_stage)
- ce = nce;
- i++;
- }
- /*
- * Compensate for loop update
- */
- i--;
-
- if (combine_merges && num_compare_stages == 2) {
- show_combined_diff(&combine.p, 2,
- dense_combined_merges,
- NULL,
- &diff_options);
- free(combine.p.path);
- continue;
- }
- free(combine.p.path);
-
- /*
- * Show the diff for the 'ce' if we found the one
- * from the desired stage.
- */
- show_unmerge(ce->name);
- if (ce_stage(ce) != diff_unmerged_stage)
- continue;
- }
-
- if (lstat(ce->name, &st) < 0) {
- if (errno != ENOENT && errno != ENOTDIR) {
- perror(ce->name);
- continue;
- }
- if (silent)
- continue;
- show_file('-', ce);
- continue;
- }
- changed = ce_match_stat(ce, &st, 0);
- if (!changed && !diff_options.find_copies_harder)
- continue;
- oldmode = ntohl(ce->ce_mode);
-
- newmode = canon_mode(st.st_mode);
- if (!trust_executable_bit &&
- S_ISREG(newmode) && S_ISREG(oldmode) &&
- ((newmode ^ oldmode) == 0111))
- newmode = oldmode;
- show_modified(oldmode, newmode,
- ce->sha1, (changed ? null_sha1 : ce->sha1),
- ce->name);
- }
- diffcore_std(&diff_options);
- diff_flush(&diff_options);
- return 0;
+ if (rev.diffopt.output_format == DIFF_FORMAT_NO_OUTPUT)
+ rev.diffopt.output_format = DIFF_FORMAT_RAW;
+ return run_diff_files(&rev, silent);
}
diff --git a/diff-index.c b/diff-index.c
index e376d65f8..86940123b 100644
--- a/diff-index.c
+++ b/diff-index.c
@@ -1,166 +1,7 @@
#include "cache.h"
-#include "tree.h"
#include "diff.h"
-
-static int cached_only = 0;
-static int match_nonexisting = 0;
-static struct diff_options diff_options;
-
-/* A file entry went away or appeared */
-static void show_file(const char *prefix,
- struct cache_entry *ce,
- unsigned char *sha1, unsigned int mode)
-{
- diff_addremove(&diff_options, prefix[0], ntohl(mode),
- sha1, ce->name, NULL);
-}
-
-static int get_stat_data(struct cache_entry *ce,
- unsigned char ** sha1p, unsigned int *modep)
-{
- unsigned char *sha1 = ce->sha1;
- unsigned int mode = ce->ce_mode;
-
- if (!cached_only) {
- static unsigned char no_sha1[20];
- int changed;
- struct stat st;
- if (lstat(ce->name, &st) < 0) {
- if (errno == ENOENT && match_nonexisting) {
- *sha1p = sha1;
- *modep = mode;
- return 0;
- }
- return -1;
- }
- changed = ce_match_stat(ce, &st, 0);
- if (changed) {
- mode = create_ce_mode(st.st_mode);
- if (!trust_executable_bit && S_ISREG(st.st_mode))
- mode = ce->ce_mode;
- sha1 = no_sha1;
- }
- }
-
- *sha1p = sha1;
- *modep = mode;
- return 0;
-}
-
-static void show_new_file(struct cache_entry *new)
-{
- unsigned char *sha1;
- unsigned int mode;
-
- /* New file in the index: it might actually be different in
- * the working copy.
- */
- if (get_stat_data(new, &sha1, &mode) < 0)
- return;
-
- show_file("+", new, sha1, mode);
-}
-
-static int show_modified(struct cache_entry *old,
- struct cache_entry *new,
- int report_missing)
-{
- unsigned int mode, oldmode;
- unsigned char *sha1;
-
- if (get_stat_data(new, &sha1, &mode) < 0) {
- if (report_missing)
- show_file("-", old, old->sha1, old->ce_mode);
- return -1;
- }
-
- oldmode = old->ce_mode;
- if (mode == oldmode && !memcmp(sha1, old->sha1, 20) &&
- !diff_options.find_copies_harder)
- return 0;
-
- mode = ntohl(mode);
- oldmode = ntohl(oldmode);
-
- diff_change(&diff_options, oldmode, mode,
- old->sha1, sha1, old->name, NULL);
- return 0;
-}
-
-static int diff_cache(struct cache_entry **ac, int entries, const char **pathspec)
-{
- while (entries) {
- struct cache_entry *ce = *ac;
- int same = (entries > 1) && ce_same_name(ce, ac[1]);
-
- if (!ce_path_match(ce, pathspec))
- goto skip_entry;
-
- switch (ce_stage(ce)) {
- case 0:
- /* No stage 1 entry? That means it's a new file */
- if (!same) {
- show_new_file(ce);
- break;
- }
- /* Show difference between old and new */
- show_modified(ac[1], ce, 1);
- break;
- case 1:
- /* No stage 3 (merge) entry? That means it's been deleted */
- if (!same) {
- show_file("-", ce, ce->sha1, ce->ce_mode);
- break;
- }
- /* We come here with ce pointing at stage 1
- * (original tree) and ac[1] pointing at stage
- * 3 (unmerged). show-modified with
- * report-missing set to false does not say the
- * file is deleted but reports true if work
- * tree does not have it, in which case we
- * fall through to report the unmerged state.
- * Otherwise, we show the differences between
- * the original tree and the work tree.
- */
- if (!cached_only && !show_modified(ce, ac[1], 0))
- break;
- /* fallthru */
- case 3:
- diff_unmerge(&diff_options, ce->name);
- break;
-
- default:
- die("impossible cache entry stage");
- }
-
-skip_entry:
- /*
- * Ignore all the different stages for this file,
- * we've handled the relevant cases now.
- */
- do {
- ac++;
- entries--;
- } while (entries && ce_same_name(ce, ac[0]));
- }
- return 0;
-}
-
-/*
- * This turns all merge entries into "stage 3". That guarantees that
- * when we read in the new tree (into "stage 1"), we won't lose sight
- * of the fact that we had unmerged entries.
- */
-static void mark_merge_entries(void)
-{
- int i;
- for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
- if (!ce_stage(ce))
- continue;
- ce->ce_flags |= htons(CE_STAGEMASK);
- }
-}
+#include "commit.h"
+#include "revision.h"
static const char diff_cache_usage[] =
"git-diff-index [-m] [--cached] "
@@ -169,85 +10,30 @@ COMMON_DIFF_OPTIONS_HELP;
int main(int argc, const char **argv)
{
- const char *tree_name = NULL;
- unsigned char sha1[20];
- const char *prefix = setup_git_directory();
- const char **pathspec = NULL;
- struct tree *tree;
- int ret;
- int allow_options = 1;
+ struct rev_info rev;
+ int match_missing = 0;
+ int cached = 0;
int i;
git_config(git_diff_config);
- diff_setup(&diff_options);
+ init_revisions(&rev);
+ rev.abbrev = 0;
+
+ argc = setup_revisions(argc, argv, &rev, NULL);
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
- int diff_opt_cnt;
-
- if (!allow_options || *arg != '-') {
- if (tree_name)
- break;
- tree_name = arg;
- continue;
- }
- if (!strcmp(arg, "--")) {
- allow_options = 0;
- continue;
- }
- if (!strcmp(arg, "-r")) {
- /* We accept the -r flag just to look like git-diff-tree */
- continue;
- }
- if (!strcmp(arg, "--cc"))
- /*
- * I _think_ "diff-index --cached HEAD" with an
- * unmerged index could show something else
- * later, but pretend --cc is the same as -p for
- * now. "git diff" uses --cc by default.
- */
- argv[i] = arg = "-p";
- diff_opt_cnt = diff_opt_parse(&diff_options, argv + i,
- argc - i);
- if (diff_opt_cnt < 0)
+ if (!strcmp(arg, "--cached"))
+ cached = 1;
+ else
usage(diff_cache_usage);
- else if (diff_opt_cnt) {
- i += diff_opt_cnt - 1;
- continue;
- }
-
- if (!strcmp(arg, "-m")) {
- match_nonexisting = 1;
- continue;
- }
- if (!strcmp(arg, "--cached")) {
- cached_only = 1;
- continue;
- }
- usage(diff_cache_usage);
}
-
- pathspec = get_pathspec(prefix, argv + i);
-
- if (diff_setup_done(&diff_options) < 0)
- usage(diff_cache_usage);
-
- if (!tree_name || get_sha1(tree_name, sha1))
+ /*
+ * Make sure there is one revision (i.e. pending object),
+ * and there is no revision filtering parameters.
+ */
+ if (!rev.pending_objects || rev.pending_objects->next ||
+ rev.max_count != -1 || rev.min_age != -1 || rev.max_age != -1)
usage(diff_cache_usage);
-
- read_cache();
-
- mark_merge_entries();
-
- tree = parse_tree_indirect(sha1);
- if (!tree)
- die("bad tree object %s", tree_name);
- if (read_tree(tree, 1, pathspec))
- die("unable to read tree object %s", tree_name);
-
- ret = diff_cache(active_cache, active_nr, pathspec);
-
- diffcore_std(&diff_options);
- diff_flush(&diff_options);
- return ret;
+ return run_diff_index(&rev, cached);
}
diff --git a/diff-lib.c b/diff-lib.c
new file mode 100644
index 000000000..2183b41b0
--- /dev/null
+++ b/diff-lib.c
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2005 Junio C Hamano
+ */
+#include "cache.h"
+#include "quote.h"
+#include "commit.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "revision.h"
+
+/*
+ * diff-files
+ */
+
+int run_diff_files(struct rev_info *revs, int silent_on_removed)
+{
+ int entries, i;
+ int diff_unmerged_stage = revs->max_count;
+
+ if (diff_unmerged_stage < 0)
+ diff_unmerged_stage = 2;
+ entries = read_cache();
+ if (entries < 0) {
+ perror("read_cache");
+ return -1;
+ }
+ for (i = 0; i < entries; i++) {
+ struct stat st;
+ unsigned int oldmode, newmode;
+ struct cache_entry *ce = active_cache[i];
+ int changed;
+
+ if (!ce_path_match(ce, revs->prune_data))
+ continue;
+
+ if (ce_stage(ce)) {
+ struct {
+ struct combine_diff_path p;
+ struct combine_diff_parent filler[5];
+ } combine;
+ int num_compare_stages = 0;
+
+ combine.p.next = NULL;
+ combine.p.len = ce_namelen(ce);
+ combine.p.path = xmalloc(combine.p.len + 1);
+ memcpy(combine.p.path, ce->name, combine.p.len);
+ combine.p.path[combine.p.len] = 0;
+ combine.p.mode = 0;
+ memset(combine.p.sha1, 0, 20);
+ memset(&combine.p.parent[0], 0,
+ sizeof(combine.filler));
+
+ while (i < entries) {
+ struct cache_entry *nce = active_cache[i];
+ int stage;
+
+ if (strcmp(ce->name, nce->name))
+ break;
+
+ /* Stage #2 (ours) is the first parent,
+ * stage #3 (theirs) is the second.
+ */
+ stage = ce_stage(nce);
+ if (2 <= stage) {
+ int mode = ntohl(nce->ce_mode);
+ num_compare_stages++;
+ memcpy(combine.p.parent[stage-2].sha1,
+ nce->sha1, 20);
+ combine.p.parent[stage-2].mode =
+ canon_mode(mode);
+ combine.p.parent[stage-2].status =
+ DIFF_STATUS_MODIFIED;
+ }
+
+ /* diff against the proper unmerged stage */
+ if (stage == diff_unmerged_stage)
+ ce = nce;
+ i++;
+ }
+ /*
+ * Compensate for loop update
+ */
+ i--;
+
+ if (revs->combine_merges && num_compare_stages == 2) {
+ show_combined_diff(&combine.p, 2,
+ revs->dense_combined_merges,
+ revs);
+ free(combine.p.path);
+ continue;
+ }
+ free(combine.p.path);
+
+ /*
+ * Show the diff for the 'ce' if we found the one
+ * from the desired stage.
+ */
+ diff_unmerge(&revs->diffopt, ce->name);
+ if (ce_stage(ce) != diff_unmerged_stage)
+ continue;
+ }
+
+ if (lstat(ce->name, &st) < 0) {
+ if (errno != ENOENT && errno != ENOTDIR) {
+ perror(ce->name);
+ continue;
+ }
+ if (silent_on_removed)
+ continue;
+ diff_addremove(&revs->diffopt, '-', ntohl(ce->ce_mode),
+ ce->sha1, ce->name, NULL);
+ continue;
+ }
+ changed = ce_match_stat(ce, &st, 0);
+ if (!changed && !revs->diffopt.find_copies_harder)
+ continue;
+ oldmode = ntohl(ce->ce_mode);
+
+ newmode = canon_mode(st.st_mode);
+ if (!trust_executable_bit &&
+ S_ISREG(newmode) && S_ISREG(oldmode) &&
+ ((newmode ^ oldmode) == 0111))
+ newmode = oldmode;
+ diff_change(&revs->diffopt, oldmode, newmode,
+ ce->sha1, (changed ? null_sha1 : ce->sha1),
+ ce->name, NULL);
+
+ }
+ diffcore_std(&revs->diffopt);
+ diff_flush(&revs->diffopt);
+ return 0;
+}
+
+/*
+ * diff-index
+ */
+
+/* A file entry went away or appeared */
+static void diff_index_show_file(struct rev_info *revs,
+ const char *prefix,
+ struct cache_entry *ce,
+ unsigned char *sha1, unsigned int mode)
+{
+ diff_addremove(&revs->diffopt, prefix[0], ntohl(mode),
+ sha1, ce->name, NULL);
+}
+
+static int get_stat_data(struct cache_entry *ce,
+ unsigned char **sha1p,
+ unsigned int *modep,
+ int cached, int match_missing)
+{
+ unsigned char *sha1 = ce->sha1;
+ unsigned int mode = ce->ce_mode;
+
+ if (!cached) {
+ static unsigned char no_sha1[20];
+ int changed;
+ struct stat st;
+ if (lstat(ce->name, &st) < 0) {
+ if (errno == ENOENT && match_missing) {
+ *sha1p = sha1;
+ *modep = mode;
+ return 0;
+ }
+ return -1;
+ }
+ changed = ce_match_stat(ce, &st, 0);
+ if (changed) {
+ mode = create_ce_mode(st.st_mode);
+ if (!trust_executable_bit && S_ISREG(st.st_mode))
+ mode = ce->ce_mode;
+ sha1 = no_sha1;
+ }
+ }
+
+ *sha1p = sha1;
+ *modep = mode;
+ return 0;
+}
+
+static void show_new_file(struct rev_info *revs,
+ struct cache_entry *new,
+ int cached, int match_missing)
+{
+ unsigned char *sha1;
+ unsigned int mode;
+
+ /* New file in the index: it might actually be different in
+ * the working copy.
+ */
+ if (get_stat_data(new, &sha1, &mode, cached, match_missing) < 0)
+ return;
+
+ diff_index_show_file(revs, "+", new, sha1, mode);
+}
+
+static int show_modified(struct rev_info *revs,
+ struct cache_entry *old,
+ struct cache_entry *new,
+ int report_missing,
+ int cached, int match_missing)
+{
+ unsigned int mode, oldmode;
+ unsigned char *sha1;
+
+ if (get_stat_data(new, &sha1, &mode, cached, match_missing) < 0) {
+ if (report_missing)
+ diff_index_show_file(revs, "-", old,
+ old->sha1, old->ce_mode);
+ return -1;
+ }
+
+ oldmode = old->ce_mode;
+ if (mode == oldmode && !memcmp(sha1, old->sha1, 20) &&
+ !revs->diffopt.find_copies_harder)
+ return 0;
+
+ mode = ntohl(mode);
+ oldmode = ntohl(oldmode);
+
+ diff_change(&revs->diffopt, oldmode, mode,
+ old->sha1, sha1, old->name, NULL);
+ return 0;
+}
+
+static int diff_cache(struct rev_info *revs,
+ struct cache_entry **ac, int entries,
+ const char **pathspec,
+ int cached, int match_missing)
+{
+ while (entries) {
+ struct cache_entry *ce = *ac;
+ int same = (entries > 1) && ce_same_name(ce, ac[1]);
+
+ if (!ce_path_match(ce, pathspec))
+ goto skip_entry;
+
+ switch (ce_stage(ce)) {
+ case 0:
+ /* No stage 1 entry? That means it's a new file */
+ if (!same) {
+ show_new_file(revs, ce, cached, match_missing);
+ break;
+ }
+ /* Show difference between old and new */
+ show_modified(revs,ac[1], ce, 1,
+ cached, match_missing);
+ break;
+ case 1:
+ /* No stage 3 (merge) entry?
+ * That means it's been deleted.
+ */
+ if (!same) {
+ diff_index_show_file(revs, "-", ce,
+ ce->sha1, ce->ce_mode);
+ break;
+ }
+ /* We come here with ce pointing at stage 1
+ * (original tree) and ac[1] pointing at stage
+ * 3 (unmerged). show-modified with
+ * report-missing set to false does not say the
+ * file is deleted but reports true if work
+ * tree does not have it, in which case we
+ * fall through to report the unmerged state.
+ * Otherwise, we show the differences between
+ * the original tree and the work tree.
+ */
+ if (!cached &&
+ !show_modified(revs, ce, ac[1], 0,
+ cached, match_missing))
+ break;
+ /* fallthru */
+ case 3:
+ diff_unmerge(&revs->diffopt, ce->name);
+ break;
+
+ default:
+ die("impossible cache entry stage");
+ }
+
+skip_entry:
+ /*
+ * Ignore all the different stages for this file,
+ * we've handled the relevant cases now.
+ */
+ do {
+ ac++;
+ entries--;
+ } while (entries && ce_same_name(ce, ac[0]));
+ }
+ return 0;
+}
+
+/*
+ * This turns all merge entries into "stage 3". That guarantees that
+ * when we read in the new tree (into "stage 1"), we won't lose sight
+ * of the fact that we had unmerged entries.
+ */
+static void mark_merge_entries(void)
+{
+ int i;
+ for (i = 0; i < active_nr; i++) {
+ struct cache_entry *ce = active_cache[i];
+ if (!ce_stage(ce))
+ continue;
+ ce->ce_flags |= htons(CE_STAGEMASK);
+ }
+}
+
+int run_diff_index(struct rev_info *revs, int cached)
+{
+ int ret;
+ struct object *ent;
+ struct tree *tree;
+ const char *tree_name;
+ int match_missing = 0;
+
+ /*
+ * Backward compatibility wart - "diff-index -m" does
+ * not mean "do not ignore merges", but totally different.
+ */
+ if (!revs->ignore_merges)
+ match_missing = 1;
+
+ if (read_cache() < 0) {
+ perror("read_cache");
+ return -1;
+ }
+ mark_merge_entries();
+
+ ent = revs->pending_objects->item;
+ tree_name = revs->pending_objects->name;
+ tree = parse_tree_indirect(ent->sha1);
+ if (!tree)
+ return error("bad tree object %s", tree_name);
+ if (read_tree(tree, 1, revs->prune_data))
+ return error("unable to read tree object %s", tree_name);
+ ret = diff_cache(revs, active_cache, active_nr, revs->prune_data,
+ cached, match_missing);
+ diffcore_std(&revs->diffopt);
+ diff_flush(&revs->diffopt);
+ return ret;
+}
diff --git a/diff-tree.c b/diff-tree.c
index d1c61c851..7207867a7 100644
--- a/diff-tree.c
+++ b/diff-tree.c
@@ -3,7 +3,7 @@
#include "commit.h"
#include "log-tree.h"
-static struct log_tree_opt log_tree_opt;
+static struct rev_info log_tree_opt;
static int diff_tree_commit_sha1(const unsigned char *sha1)
{
@@ -62,47 +62,21 @@ int main(int argc, const char **argv)
{
int nr_sha1;
char line[1000];
- unsigned char sha1[2][20];
- const char *prefix = setup_git_directory();
- static struct log_tree_opt *opt = &log_tree_opt;
+ struct object *tree1, *tree2;
+ static struct rev_info *opt = &log_tree_opt;
+ struct object_list *list;
int read_stdin = 0;
git_config(git_diff_config);
nr_sha1 = 0;
- init_log_tree_opt(opt);
+ init_revisions(opt);
+ opt->abbrev = 0;
+ opt->diff = 1;
+ argc = setup_revisions(argc, argv, opt, NULL);
- for (;;) {
- int opt_cnt;
- const char *arg;
+ while (--argc > 0) {
+ const char *arg = *++argv;
- argv++;
- argc--;
- arg = *argv;
- if (!arg)
- break;
-
- if (*arg != '-') {
- if (nr_sha1 < 2 && !get_sha1(arg, sha1[nr_sha1])) {
- nr_sha1++;
- continue;
- }
- break;
- }
-
- opt_cnt = log_tree_opt_parse(opt, argv, argc);
- if (opt_cnt < 0)
- usage(diff_tree_usage);
- else if (opt_cnt) {
- argv += opt_cnt - 1;
- argc -= opt_cnt - 1;
- continue;
- }
-
- if (!strcmp(arg, "--")) {
- argv++;
- argc--;
- break;
- }
if (!strcmp(arg, "--stdin")) {
read_stdin = 1;
continue;
@@ -110,15 +84,36 @@ int main(int argc, const char **argv)
usage(diff_tree_usage);
}
- if (opt->combine_merges)
- opt->ignore_merges = 0;
-
- /* We can only do dense combined merges with diff output */
- if (opt->dense_combined_merges)
- opt->diffopt.output_format = DIFF_FORMAT_PATCH;
-
- diff_tree_setup_paths(get_pathspec(prefix, argv), &opt->diffopt);
- diff_setup_done(&opt->diffopt);
+ /*
+ * NOTE! "setup_revisions()" will have inserted the revisions
+ * it parsed in reverse order. So if you do
+ *
+ * git-diff-tree a b
+ *
+ * the commit list will be "b" -> "a" -> NULL, so we reverse
+ * the order of the objects if the first one is not marked
+ * UNINTERESTING.
+ */
+ nr_sha1 = 0;
+ list = opt->pending_objects;
+ if (list) {
+ nr_sha1++;
+ tree1 = list->item;
+ list = list->next;
+ if (list) {
+ nr_sha1++;
+ tree2 = tree1;
+ tree1 = list->item;
+ if (list->next)
+ usage(diff_tree_usage);
+ /* Switch them around if the second one was uninteresting.. */
+ if (tree2->flags & UNINTERESTING) {
+ struct object *tmp = tree2;
+ tree2 = tree1;
+ tree1 = tmp;
+ }
+ }
+ }
switch (nr_sha1) {
case 0:
@@ -126,10 +121,12 @@ int main(int argc, const char **argv)
usage(diff_tree_usage);
break;
case 1:
- diff_tree_commit_sha1(sha1[0]);
+ diff_tree_commit_sha1(tree1->sha1);
break;
case 2:
- diff_tree_sha1(sha1[0], sha1[1], "", &opt->diffopt);
+ diff_tree_sha1(tree1->sha1,
+ tree2->sha1,
+ "", &opt->diffopt);
log_tree_diff_flush(opt);
break;
}
diff --git a/diff.c b/diff.c
index 903afa168..13b216f27 100644
--- a/diff.c
+++ b/diff.c
@@ -195,6 +195,56 @@ static int fn_out(void *priv, mmbuffer_t *mb, int nbuf)
return 0;
}
+static char *pprint_rename(const char *a, const char *b)
+{
+ const char *old = a;
+ const char *new = b;
+ char *name = NULL;
+ int pfx_length, sfx_length;
+ int len_a = strlen(a);
+ int len_b = strlen(b);
+
+ /* Find common prefix */
+ pfx_length = 0;
+ while (*old && *new && *old == *new) {
+ if (*old == '/')
+ pfx_length = old - a + 1;
+ old++;
+ new++;
+ }
+
+ /* Find common suffix */
+ old = a + len_a;
+ new = b + len_b;
+ sfx_length = 0;
+ while (a <= old && b <= new && *old == *new) {
+ if (*old == '/')
+ sfx_length = len_a - (old - a);
+ old--;
+ new--;
+ }
+
+ /*
+ * pfx{mid-a => mid-b}sfx
+ * {pfx-a => pfx-b}sfx
+ * pfx{sfx-a => sfx-b}
+ * name-a => name-b
+ */
+ if (pfx_length + sfx_length) {
+ name = xmalloc(len_a + len_b - pfx_length - sfx_length + 7);
+ sprintf(name, "%.*s{%.*s => %.*s}%s",
+ pfx_length, a,
+ len_a - pfx_length - sfx_length, a + pfx_length,
+ len_b - pfx_length - sfx_length, b + pfx_length,
+ a + len_a - sfx_length);
+ }
+ else {
+ name = xmalloc(len_a + len_b + 5);
+ sprintf(name, "%s => %s", a, b);
+ }
+ return name;
+}
+
struct diffstat_t {
struct xdiff_emit_state xm;
@@ -204,12 +254,14 @@ struct diffstat_t {
char *name;
unsigned is_unmerged:1;
unsigned is_binary:1;
+ unsigned is_renamed:1;
unsigned int added, deleted;
} **files;
};
static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat,
- const char *name)
+ const char *name_a,
+ const char *name_b)
{
struct diffstat_file *x;
x = xcalloc(sizeof (*x), 1);
@@ -219,7 +271,12 @@ static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat,
diffstat->alloc * sizeof(x));
}
diffstat->files[diffstat->nr++] = x;
- x->name = strdup(name);
+ if (name_b) {
+ x->name = pprint_rename(name_a, name_b);
+ x->is_renamed = 1;
+ }
+ else
+ x->name = strdup(name_a);
return x;
}
@@ -305,7 +362,8 @@ static void show_stats(struct diffstat_t* data)
printf(" %s%-*s | Unmerged\n", prefix, len, name);
goto free_diffstat_file;
}
- else if (added + deleted == 0) {
+ else if (!data->files[i]->is_renamed &&
+ (added + deleted == 0)) {
total_files--;
goto free_diffstat_file;
}
@@ -425,13 +483,14 @@ static void builtin_diff(const char *name_a,
}
static void builtin_diffstat(const char *name_a, const char *name_b,
- struct diff_filespec *one, struct diff_filespec *two,
- struct diffstat_t *diffstat)
+ struct diff_filespec *one,
+ struct diff_filespec *two,
+ struct diffstat_t *diffstat)
{
mmfile_t mf1, mf2;
struct diffstat_file *data;
- data = diffstat_add(diffstat, name_a ? name_a : name_b);
+ data = diffstat_add(diffstat, name_a, name_b);
if (!one || !two) {
data->is_unmerged = 1;
@@ -992,7 +1051,7 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
}
static void run_diffstat(struct diff_filepair *p, struct diff_options *o,
- struct diffstat_t *diffstat)
+ struct diffstat_t *diffstat)
{
const char *name;
const char *other;
@@ -1036,8 +1095,7 @@ int diff_setup_done(struct diff_options *options)
* recursive bits for other formats here.
*/
if ((options->output_format == DIFF_FORMAT_PATCH) ||
- (options->output_format == DIFF_FORMAT_DIFFSTAT) ||
- (options->with_stat))
+ (options->output_format == DIFF_FORMAT_DIFFSTAT))
options->recursive = 1;
if (options->detect_rename && options->rename_limit < 0)
@@ -1375,7 +1433,7 @@ static void diff_flush_patch(struct diff_filepair *p, struct diff_options *o)
}
static void diff_flush_stat(struct diff_filepair *p, struct diff_options *o,
- struct diffstat_t *diffstat)
+ struct diffstat_t *diffstat)
{
if (diff_unmodified_pair(p))
return;
@@ -1560,7 +1618,7 @@ void diff_flush(struct diff_options *options)
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
flush_one_pair(p, DIFF_FORMAT_DIFFSTAT, options,
- diffstat);
+ diffstat);
}
show_stats(diffstat);
free(diffstat);
diff --git a/diff.h b/diff.h
index f783baef1..7150b9038 100644
--- a/diff.h
+++ b/diff.h
@@ -6,6 +6,7 @@
#include "tree-walk.h"
+struct rev_info;
struct diff_options;
typedef void (*change_fn_t)(struct diff_options *options,
@@ -27,10 +28,11 @@ struct diff_options {
with_raw:1,
with_stat:1,
tree_in_recursive:1,
- full_index:1;
+ full_index:1,
+ silent_on_remove:1,
+ find_copies_harder:1;
int break_opt;
int detect_rename;
- int find_copies_harder;
int line_termination;
int output_format;
int pickaxe_opts;
@@ -70,11 +72,10 @@ struct combine_diff_path {
(sizeof(struct combine_diff_path) + \
sizeof(struct combine_diff_parent) * (n) + (l) + 1)
-extern int show_combined_diff(struct combine_diff_path *elem, int num_parent,
- int dense, const char *header,
- struct diff_options *);
+extern void show_combined_diff(struct combine_diff_path *elem, int num_parent,
+ int dense, struct rev_info *);
-extern const char *diff_tree_combined_merge(const unsigned char *sha1, const char *, int, struct diff_options *opt);
+extern void diff_tree_combined_merge(const unsigned char *sha1, int, struct rev_info *);
extern void diff_addremove(struct diff_options *,
int addremove,
@@ -168,4 +169,8 @@ extern void diff_flush(struct diff_options*);
extern const char *diff_unique_abbrev(const unsigned char *, int);
+extern int run_diff_files(struct rev_info *revs, int silent_on_removed);
+
+extern int run_diff_index(struct rev_info *revs, int cached);
+
#endif /* DIFF_H */
diff --git a/git-repack.sh b/git-repack.sh
index a5d349fd0..e0c9f323c 100755
--- a/git-repack.sh
+++ b/git-repack.sh
@@ -5,9 +5,9 @@
USAGE='[-a] [-d] [-f] [-l] [-n] [-q]'
. git-sh-setup
-
+
no_update_info= all_into_one= remove_redundant=
-local= quiet= no_reuse_delta=
+local= quiet= no_reuse_delta= extra=
while case "$#" in 0) break ;; esac
do
case "$1" in
@@ -17,6 +17,8 @@ do
-q) quiet=-q ;;
-f) no_reuse_delta=--no-reuse-delta ;;
-l) local=--local ;;
+ --window=*) extra="$extra $1" ;;
+ --depth=*) extra="$extra $1" ;;
*) usage ;;
esac
shift
@@ -40,7 +42,7 @@ case ",$all_into_one," in
find . -type f \( -name '*.pack' -o -name '*.idx' \) -print`
;;
esac
-pack_objects="$pack_objects $local $quiet $no_reuse_delta"
+pack_objects="$pack_objects $local $quiet $no_reuse_delta$extra"
name=$(git-rev-list --objects --all $rev_list 2>&1 |
git-pack-objects --non-empty $pack_objects .tmp-pack) ||
exit 1
diff --git a/git.c b/git.c
index 5209b04de..aa2b814d9 100644
--- a/git.c
+++ b/git.c
@@ -11,215 +11,8 @@
#include <sys/ioctl.h>
#include "git-compat-util.h"
#include "exec_cmd.h"
-#include "common-cmds.h"
-#include "cache.h"
-#include "commit.h"
-#include "diff.h"
-#include "revision.h"
-#include "log-tree.h"
-
-#ifndef PATH_MAX
-# define PATH_MAX 4096
-#endif
-
-static const char git_usage[] =
- "Usage: git [--version] [--exec-path[=GIT_EXEC_PATH]] [--help] COMMAND [ ARGS ]";
-
-/* most gui terms set COLUMNS (although some don't export it) */
-static int term_columns(void)
-{
- char *col_string = getenv("COLUMNS");
- int n_cols = 0;
-
- if (col_string && (n_cols = atoi(col_string)) > 0)
- return n_cols;
-
-#ifdef TIOCGWINSZ
- {
- struct winsize ws;
- if (!ioctl(1, TIOCGWINSZ, &ws)) {
- if (ws.ws_col)
- return ws.ws_col;
- }
- }
-#endif
-
- return 80;
-}
-
-static void oom(void)
-{
- fprintf(stderr, "git: out of memory\n");
- exit(1);
-}
-
-static inline void mput_char(char c, unsigned int num)
-{
- while(num--)
- putchar(c);
-}
-
-static struct cmdname {
- size_t len;
- char name[1];
-} **cmdname;
-static int cmdname_alloc, cmdname_cnt;
-
-static void add_cmdname(const char *name, int len)
-{
- struct cmdname *ent;
- if (cmdname_alloc <= cmdname_cnt) {
- cmdname_alloc = cmdname_alloc + 200;
- cmdname = realloc(cmdname, cmdname_alloc * sizeof(*cmdname));
- if (!cmdname)
- oom();
- }
- ent = malloc(sizeof(*ent) + len);
- if (!ent)
- oom();
- ent->len = len;
- memcpy(ent->name, name, len);
- ent->name[len] = 0;
- cmdname[cmdname_cnt++] = ent;
-}
-
-static int cmdname_compare(const void *a_, const void *b_)
-{
- struct cmdname *a = *(struct cmdname **)a_;
- struct cmdname *b = *(struct cmdname **)b_;
- return strcmp(a->name, b->name);
-}
-
-static void pretty_print_string_list(struct cmdname **cmdname, int longest)
-{
- int cols = 1, rows;
- int space = longest + 1; /* min 1 SP between words */
- int max_cols = term_columns() - 1; /* don't print *on* the edge */
- int i, j;
-
- if (space < max_cols)
- cols = max_cols / space;
- rows = (cmdname_cnt + cols - 1) / cols;
-
- qsort(cmdname, cmdname_cnt, sizeof(*cmdname), cmdname_compare);
-
- for (i = 0; i < rows; i++) {
- printf(" ");
-
- for (j = 0; j < cols; j++) {
- int n = j * rows + i;
- int size = space;
- if (n >= cmdname_cnt)
- break;
- if (j == cols-1 || n + rows >= cmdname_cnt)
- size = 1;
- printf("%-*s", size, cmdname[n]->name);
- }
- putchar('\n');
- }
-}
-
-static void list_commands(const char *exec_path, const char *pattern)
-{
- unsigned int longest = 0;
- char path[PATH_MAX];
- int dirlen;
- DIR *dir = opendir(exec_path);
- struct dirent *de;
-
- if (!dir) {
- fprintf(stderr, "git: '%s': %s\n", exec_path, strerror(errno));
- exit(1);
- }
-
- dirlen = strlen(exec_path);
- if (PATH_MAX - 20 < dirlen) {
- fprintf(stderr, "git: insanely long exec-path '%s'\n",
- exec_path);
- exit(1);
- }
-
- memcpy(path, exec_path, dirlen);
- path[dirlen++] = '/';
-
- while ((de = readdir(dir)) != NULL) {
- struct stat st;
- int entlen;
-
- if (strncmp(de->d_name, "git-", 4))
- continue;
- strcpy(path+dirlen, de->d_name);
- if (stat(path, &st) || /* stat, not lstat */
- !S_ISREG(st.st_mode) ||
- !(st.st_mode & S_IXUSR))
- continue;
-
- entlen = strlen(de->d_name);
- if (4 < entlen && !strcmp(de->d_name + entlen - 4, ".exe"))
- entlen -= 4;
-
- if (longest < entlen)
- longest = entlen;
-
- add_cmdname(de->d_name + 4, entlen-4);
- }
- closedir(dir);
-
- printf("git commands available in '%s'\n", exec_path);
- printf("----------------------------");
- mput_char('-', strlen(exec_path));
- putchar('\n');
- pretty_print_string_list(cmdname, longest - 4);
- putchar('\n');
-}
-
-static void list_common_cmds_help(void)
-{
- int i, longest = 0;
-
- for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
- if (longest < strlen(common_cmds[i].name))
- longest = strlen(common_cmds[i].name);
- }
-
- puts("The most commonly used git commands are:");
- for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
- printf(" %s", common_cmds[i].name);
- mput_char(' ', longest - strlen(common_cmds[i].name) + 4);
- puts(common_cmds[i].help);
- }
- puts("(use 'git help -a' to get a list of all installed git commands)");
-}
-
-#ifdef __GNUC__
-static void cmd_usage(int show_all, const char *exec_path, const char *fmt, ...)
- __attribute__((__format__(__printf__, 3, 4), __noreturn__));
-#endif
-static void cmd_usage(int show_all, const char *exec_path, const char *fmt, ...)
-{
- if (fmt) {
- va_list ap;
-
- va_start(ap, fmt);
- printf("git: ");
- vprintf(fmt, ap);
- va_end(ap);
- putchar('\n');
- }
- else
- puts(git_usage);
-
- if (exec_path) {
- putchar('\n');
- if (show_all)
- list_commands(exec_path, "git-*");
- else
- list_common_cmds_help();
- }
-
- exit(1);
-}
+#include "builtin.h"
static void prepend_to_path(const char *dir, int len)
{
@@ -240,163 +33,7 @@ static void prepend_to_path(const char *dir, int len)
setenv("PATH", path, 1);
}
-static void show_man_page(const char *git_cmd)
-{
- const char *page;
-
- if (!strncmp(git_cmd, "git", 3))
- page = git_cmd;
- else {
- int page_len = strlen(git_cmd) + 4;
- char *p = malloc(page_len + 1);
- strcpy(p, "git-");
- strcpy(p + 4, git_cmd);
- p[page_len] = 0;
- page = p;
- }
-
- execlp("man", "man", page, NULL);
-}
-
-static int cmd_version(int argc, const char **argv, char **envp)
-{
- printf("git version %s\n", GIT_VERSION);
- return 0;
-}
-
-static int cmd_help(int argc, const char **argv, char **envp)
-{
- const char *help_cmd = argv[1];
- if (!help_cmd)
- cmd_usage(0, git_exec_path(), NULL);
- else if (!strcmp(help_cmd, "--all") || !strcmp(help_cmd, "-a"))
- cmd_usage(1, git_exec_path(), NULL);
- else
- show_man_page(help_cmd);
- return 0;
-}
-
-#define LOGSIZE (65536)
-
-static int cmd_log(int argc, const char **argv, char **envp)
-{
- struct rev_info rev;
- struct commit *commit;
- char *buf = xmalloc(LOGSIZE);
- static enum cmit_fmt commit_format = CMIT_FMT_DEFAULT;
- int abbrev = DEFAULT_ABBREV;
- int abbrev_commit = 0;
- const char *commit_prefix = "commit ";
- struct log_tree_opt opt;
- int shown = 0;
- int do_diff = 0;
- int full_diff = 0;
-
- init_log_tree_opt(&opt);
- argc = setup_revisions(argc, argv, &rev, "HEAD");
- while (1 < argc) {
- const char *arg = argv[1];
- if (!strncmp(arg, "--pretty", 8)) {
- commit_format = get_commit_format(arg + 8);
- if (commit_format == CMIT_FMT_ONELINE)
- commit_prefix = "";
- }
- else if (!strcmp(arg, "--no-abbrev")) {
- abbrev = 0;
- }
- else if (!strcmp(arg, "--abbrev")) {
- abbrev = DEFAULT_ABBREV;
- }
- else if (!strcmp(arg, "--abbrev-commit")) {
- abbrev_commit = 1;
- }
- else if (!strncmp(arg, "--abbrev=", 9)) {
- abbrev = strtoul(arg + 9, NULL, 10);
- if (abbrev && abbrev < MINIMUM_ABBREV)
- abbrev = MINIMUM_ABBREV;
- else if (40 < abbrev)
- abbrev = 40;
- }
- else if (!strcmp(arg, "--full-diff")) {
- do_diff = 1;
- full_diff = 1;
- }
- else {
- int cnt = log_tree_opt_parse(&opt, argv+1, argc-1);
- if (0 < cnt) {
- do_diff = 1;
- argv += cnt;
- argc -= cnt;
- continue;
- }
- die("unrecognized argument: %s", arg);
- }
-
- argc--; argv++;
- }
-
- if (do_diff) {
- opt.diffopt.abbrev = abbrev;
- opt.verbose_header = 0;
- opt.always_show_header = 0;
- opt.no_commit_id = 1;
- if (opt.combine_merges)
- opt.ignore_merges = 0;
- if (opt.dense_combined_merges)
- opt.diffopt.output_format = DIFF_FORMAT_PATCH;
- if (!full_diff && rev.prune_data)
- diff_tree_setup_paths(rev.prune_data, &opt.diffopt);
- diff_setup_done(&opt.diffopt);
- }
-
- prepare_revision_walk(&rev);
- setup_pager();
- while ((commit = get_revision(&rev)) != NULL) {
- if (shown && do_diff && commit_format != CMIT_FMT_ONELINE)
- putchar('\n');
- fputs(commit_prefix, stdout);
- if (abbrev_commit && abbrev)
- fputs(find_unique_abbrev(commit->object.sha1, abbrev),
- stdout);
- else
- fputs(sha1_to_hex(commit->object.sha1), stdout);
- if (rev.parents) {
- struct commit_list *parents = commit->parents;
- while (parents) {
- struct object *o = &(parents->item->object);
- parents = parents->next;
- if (o->flags & TMP_MARK)
- continue;
- printf(" %s", sha1_to_hex(o->sha1));
- o->flags |= TMP_MARK;
- }
- /* TMP_MARK is a general purpose flag that can
- * be used locally, but the user should clean
- * things up after it is done with them.
- */
- for (parents = commit->parents;
- parents;
- parents = parents->next)
- parents->item->object.flags &= ~TMP_MARK;
- }
- if (commit_format == CMIT_FMT_ONELINE)
- putchar(' ');
- else
- putchar('\n');
- pretty_print_commit(commit_format, commit, ~0, buf,
- LOGSIZE, abbrev);
- printf("%s\n", buf);
- if (do_diff) {
- printf("---\n");
- log_tree_commit(&opt, commit);
- }
- shown = 1;
- free(commit->buffer);
- commit->buffer = NULL;
- }
- free(buf);
- return 0;
-}
+const char git_version_string[] = GIT_VERSION;
static void handle_internal_command(int argc, const char **argv, char **envp)
{
@@ -408,6 +45,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "version", cmd_version },
{ "help", cmd_help },
{ "log", cmd_log },
+ { "whatchanged", cmd_whatchanged },
+ { "show", cmd_show },
};
int i;
diff --git a/gitk b/gitk
index 87e71629a..5362b76be 100755
--- a/gitk
+++ b/gitk
@@ -16,22 +16,6 @@ proc gitdir {} {
}
}
-proc parse_args {rargs} {
- global parsed_args
-
- if {[catch {
- set parse_args [concat --default HEAD $rargs]
- set parsed_args [split [eval exec git-rev-parse $parse_args] "\n"]
- }]} {
- # if git-rev-parse failed for some reason...
- if {$rargs == {}} {
- set rargs HEAD
- }
- set parsed_args $rargs
- }
- return $parsed_args
-}
-
proc start_rev_list {rlargs} {
global startmsecs nextupdate ncmupdate
global commfd leftover tclencoding datemode
@@ -46,7 +30,7 @@ proc start_rev_list {rlargs} {
}
if {[catch {
set commfd [open [concat | git-rev-list --header $order \
- --parents --boundary $rlargs] r]
+ --parents --boundary --default HEAD $rlargs] r]
} err]} {
puts stderr "Error executing git-rev-list: $err"
exit 1
@@ -65,7 +49,7 @@ proc getcommits {rargs} {
global phase canv mainfont
set phase getcommits
- start_rev_list [parse_args $rargs]
+ start_rev_list $rargs
$canv delete all
$canv create text 3 3 -anchor nw -text "Reading commits..." \
-font $mainfont -tags textitems
diff --git a/http-push.c b/http-push.c
index 114d01bce..b4327d924 100644
--- a/http-push.c
+++ b/http-push.c
@@ -2498,6 +2498,7 @@ int main(int argc, char **argv)
commit_argv[3] = old_sha1_hex;
commit_argc++;
}
+ init_revisions(&revs);
setup_revisions(commit_argc, commit_argv, &revs, NULL);
free(new_sha1_hex);
if (old_sha1_hex) {
diff --git a/log-tree.c b/log-tree.c
index 3d404824a..9634c4677 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -3,59 +3,58 @@
#include "commit.h"
#include "log-tree.h"
-void init_log_tree_opt(struct log_tree_opt *opt)
+void show_log(struct rev_info *opt, struct log_info *log, const char *sep)
{
- memset(opt, 0, sizeof *opt);
- opt->ignore_merges = 1;
- opt->header_prefix = "";
- opt->commit_format = CMIT_FMT_RAW;
- diff_setup(&opt->diffopt);
-}
-
-int log_tree_opt_parse(struct log_tree_opt *opt, const char **av, int ac)
-{
- const char *arg;
- int cnt = diff_opt_parse(&opt->diffopt, av, ac);
- if (0 < cnt)
- return cnt;
- arg = *av;
- if (!strcmp(arg, "-r"))
- opt->diffopt.recursive = 1;
- else if (!strcmp(arg, "-t")) {
- opt->diffopt.recursive = 1;
- opt->diffopt.tree_in_recursive = 1;
- }
- else if (!strcmp(arg, "-m"))
- opt->ignore_merges = 0;
- else if (!strcmp(arg, "-c"))
- opt->combine_merges = 1;
- else if (!strcmp(arg, "--cc")) {
- opt->dense_combined_merges = 1;
- opt->combine_merges = 1;
- }
- else if (!strcmp(arg, "-v")) {
- opt->verbose_header = 1;
- opt->header_prefix = "diff-tree ";
- }
- else if (!strncmp(arg, "--pretty", 8)) {
- opt->verbose_header = 1;
- opt->header_prefix = "diff-tree ";
- opt->commit_format = get_commit_format(arg+8);
+ static char this_header[16384];
+ struct commit *commit = log->commit, *parent = log->parent;
+ int abbrev = opt->diffopt.abbrev;
+ int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40;
+ const char *extra;
+ int len;
+
+ opt->loginfo = NULL;
+ if (!opt->verbose_header) {
+ puts(sha1_to_hex(commit->object.sha1));
+ return;
}
- else if (!strcmp(arg, "--root"))
- opt->show_root_diff = 1;
- else if (!strcmp(arg, "--no-commit-id"))
- opt->no_commit_id = 1;
- else if (!strcmp(arg, "--always"))
- opt->always_show_header = 1;
- else
- return 0;
- return 1;
+
+ /*
+ * The "oneline" format has several special cases:
+ * - The pretty-printed commit lacks a newline at the end
+ * of the buffer, but we do want to make sure that we
+ * have a newline there. If the separator isn't already
+ * a newline, add an extra one.
+ * - unlike other log messages, the one-line format does
+ * not have an empty line between entries.
+ */
+ extra = "";
+ if (*sep != '\n' && opt->commit_format == CMIT_FMT_ONELINE)
+ extra = "\n";
+ if (opt->shown_one && opt->commit_format != CMIT_FMT_ONELINE)
+ putchar('\n');
+ opt->shown_one = 1;
+
+ /*
+ * Print header line of header..
+ */
+ printf("%s%s",
+ opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ",
+ diff_unique_abbrev(commit->object.sha1, abbrev_commit));
+ if (parent)
+ printf(" (from %s)", diff_unique_abbrev(parent->object.sha1, abbrev_commit));
+ putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n');
+
+ /*
+ * And then the pretty-printed message itself
+ */
+ len = pretty_print_commit(opt->commit_format, commit, ~0u, this_header, sizeof(this_header), abbrev);
+ printf("%s%s%s", this_header, extra, sep);
}
-int log_tree_diff_flush(struct log_tree_opt *opt)
+int log_tree_diff_flush(struct rev_info *opt)
{
diffcore_std(&opt->diffopt);
+
if (diff_queue_is_empty()) {
int saved_fmt = opt->diffopt.output_format;
opt->diffopt.output_format = DIFF_FORMAT_NO_OUTPUT;
@@ -63,17 +62,14 @@ int log_tree_diff_flush(struct log_tree_opt *opt)
opt->diffopt.output_format = saved_fmt;
return 0;
}
- if (opt->header) {
- if (!opt->no_commit_id)
- printf("%s%c", opt->header,
- opt->diffopt.line_termination);
- opt->header = NULL;
- }
+
+ if (opt->loginfo && !opt->no_commit_id)
+ show_log(opt, opt->loginfo, opt->diffopt.with_stat ? "---\n" : "\n");
diff_flush(&opt->diffopt);
return 1;
}
-static int diff_root_tree(struct log_tree_opt *opt,
+static int diff_root_tree(struct rev_info *opt,
const unsigned char *new, const char *base)
{
int retval;
@@ -93,83 +89,78 @@ static int diff_root_tree(struct log_tree_opt *opt,
return retval;
}
-static const char *generate_header(struct log_tree_opt *opt,
- const unsigned char *commit_sha1,
- const unsigned char *parent_sha1,
- const struct commit *commit)
-{
- static char this_header[16384];
- int offset;
- unsigned long len;
- int abbrev = opt->diffopt.abbrev;
- const char *msg = commit->buffer;
-
- if (!opt->verbose_header)
- return sha1_to_hex(commit_sha1);
-
- len = strlen(msg);
-
- offset = sprintf(this_header, "%s%s ",
- opt->header_prefix,
- diff_unique_abbrev(commit_sha1, abbrev));
- if (commit_sha1 != parent_sha1)
- offset += sprintf(this_header + offset, "(from %s)\n",
- parent_sha1
- ? diff_unique_abbrev(parent_sha1, abbrev)
- : "root");
- else
- offset += sprintf(this_header + offset, "(from parents)\n");
- offset += pretty_print_commit(opt->commit_format, commit, len,
- this_header + offset,
- sizeof(this_header) - offset, abbrev);
- if (opt->always_show_header) {
- puts(this_header);
- return NULL;
- }
- return this_header;
-}
-
-static int do_diff_combined(struct log_tree_opt *opt, struct commit *commit)
+static int do_diff_combined(struct rev_info *opt, struct commit *commit)
{
unsigned const char *sha1 = commit->object.sha1;
- opt->header = generate_header(opt, sha1, sha1, commit);
- opt->header = diff_tree_combined_merge(sha1, opt->header,
- opt->dense_combined_merges,
- &opt->diffopt);
- if (!opt->header && opt->verbose_header)
- opt->header_prefix = "\ndiff-tree ";
- return 0;
+ diff_tree_combined_merge(sha1, opt->dense_combined_merges, opt);
+ return !opt->loginfo;
}
-int log_tree_commit(struct log_tree_opt *opt, struct commit *commit)
+/*
+ * Show the diff of a commit.
+ *
+ * Return true if we printed any log info messages
+ */
+static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log_info *log)
{
+ int showed_log;
struct commit_list *parents;
unsigned const char *sha1 = commit->object.sha1;
+ if (!opt->diff)
+ return 0;
+
/* Root commit? */
- if (opt->show_root_diff && !commit->parents) {
- opt->header = generate_header(opt, sha1, NULL, commit);
- diff_root_tree(opt, sha1, "");
+ parents = commit->parents;
+ if (!parents) {
+ if (opt->show_root_diff)
+ diff_root_tree(opt, sha1, "");
+ return !opt->loginfo;
}
/* More than one parent? */
- if (commit->parents && commit->parents->next) {
+ if (parents && parents->next) {
if (opt->ignore_merges)
return 0;
else if (opt->combine_merges)
return do_diff_combined(opt, commit);
+
+ /* If we show individual diffs, show the parent info */
+ log->parent = parents->item;
}
- for (parents = commit->parents; parents; parents = parents->next) {
+ showed_log = 0;
+ for (;;) {
struct commit *parent = parents->item;
- unsigned const char *psha1 = parent->object.sha1;
- opt->header = generate_header(opt, sha1, psha1, commit);
- diff_tree_sha1(psha1, sha1, "", &opt->diffopt);
- log_tree_diff_flush(opt);
- if (!opt->header && opt->verbose_header)
- opt->header_prefix = "\ndiff-tree ";
+ diff_tree_sha1(parent->object.sha1, sha1, "", &opt->diffopt);
+ log_tree_diff_flush(opt);
+
+ showed_log |= !opt->loginfo;
+
+ /* Set up the log info for the next parent, if any.. */
+ parents = parents->next;
+ if (!parents)
+ break;
+ log->parent = parents->item;
+ opt->loginfo = log;
+ }
+ return showed_log;
+}
+
+int log_tree_commit(struct rev_info *opt, struct commit *commit)
+{
+ struct log_info log;
+
+ log.commit = commit;
+ log.parent = NULL;
+ opt->loginfo = &log;
+
+ if (!log_tree_diff(opt, commit, &log) && opt->loginfo && opt->always_show_header) {
+ log.parent = NULL;
+ show_log(opt, opt->loginfo, "");
}
+ opt->loginfo = NULL;
return 0;
}
diff --git a/log-tree.h b/log-tree.h
index da166c6f2..a26e4841f 100644
--- a/log-tree.h
+++ b/log-tree.h
@@ -1,23 +1,16 @@
#ifndef LOG_TREE_H
#define LOG_TREE_H
-struct log_tree_opt {
- struct diff_options diffopt;
- int show_root_diff;
- int no_commit_id;
- int verbose_header;
- int ignore_merges;
- int combine_merges;
- int dense_combined_merges;
- int always_show_header;
- const char *header_prefix;
- const char *header;
- enum cmit_fmt commit_format;
+#include "revision.h"
+
+struct log_info {
+ struct commit *commit, *parent;
};
-void init_log_tree_opt(struct log_tree_opt *);
-int log_tree_diff_flush(struct log_tree_opt *);
-int log_tree_commit(struct log_tree_opt *, struct commit *);
-int log_tree_opt_parse(struct log_tree_opt *, const char **, int);
+void init_log_tree_opt(struct rev_info *);
+int log_tree_diff_flush(struct rev_info *);
+int log_tree_commit(struct rev_info *, struct commit *);
+int log_tree_opt_parse(struct rev_info *, const char **, int);
+void show_log(struct rev_info *opt, struct log_info *log, const char *sep);
#endif
diff --git a/pager.c b/pager.c
index f7b8e7871..9a3093901 100644
--- a/pager.c
+++ b/pager.c
@@ -21,7 +21,7 @@ void setup_pager(void)
return;
if (!pager)
pager = "less";
- else if (!*pager)
+ else if (!*pager || !strcmp(pager, "cat"))
return;
if (pipe(fd) < 0)
diff --git a/rev-list.c b/rev-list.c
index a8fe83c5d..8b0ec388f 100644
--- a/rev-list.c
+++ b/rev-list.c
@@ -39,24 +39,21 @@ static const char rev_list_usage[] =
struct rev_info revs;
static int bisect_list = 0;
-static int verbose_header = 0;
-static int abbrev = DEFAULT_ABBREV;
-static int abbrev_commit = 0;
static int show_timestamp = 0;
static int hdr_termination = 0;
-static const char *commit_prefix = "";
-static enum cmit_fmt commit_format = CMIT_FMT_RAW;
+static const char *header_prefix;
static void show_commit(struct commit *commit)
{
if (show_timestamp)
printf("%lu ", commit->date);
- if (commit_prefix[0])
- fputs(commit_prefix, stdout);
+ if (header_prefix)
+ fputs(header_prefix, stdout);
if (commit->object.flags & BOUNDARY)
putchar('-');
- if (abbrev_commit && abbrev)
- fputs(find_unique_abbrev(commit->object.sha1, abbrev), stdout);
+ if (revs.abbrev_commit && revs.abbrev)
+ fputs(find_unique_abbrev(commit->object.sha1, revs.abbrev),
+ stdout);
else
fputs(sha1_to_hex(commit->object.sha1), stdout);
if (revs.parents) {
@@ -78,14 +75,16 @@ static void show_commit(struct commit *commit)
parents = parents->next)
parents->item->object.flags &= ~TMP_MARK;
}
- if (commit_format == CMIT_FMT_ONELINE)
+ if (revs.commit_format == CMIT_FMT_ONELINE)
putchar(' ');
else
putchar('\n');
- if (verbose_header) {
+ if (revs.verbose_header) {
static char pretty_header[16384];
- pretty_print_commit(commit_format, commit, ~0, pretty_header, sizeof(pretty_header), abbrev);
+ pretty_print_commit(revs.commit_format, commit, ~0,
+ pretty_header, sizeof(pretty_header),
+ revs.abbrev);
printf("%s%c", pretty_header, hdr_termination);
}
fflush(stdout);
@@ -297,58 +296,16 @@ int main(int argc, const char **argv)
struct commit_list *list;
int i;
+ init_revisions(&revs);
+ revs.abbrev = 0;
+ revs.commit_format = CMIT_FMT_UNSPECIFIED;
argc = setup_revisions(argc, argv, &revs, NULL);
for (i = 1 ; i < argc; i++) {
const char *arg = argv[i];
- /* accept -<digit>, like traditilnal "head" */
- if ((*arg == '-') && isdigit(arg[1])) {
- revs.max_count = atoi(arg + 1);
- continue;
- }
- if (!strcmp(arg, "-n")) {
- if (++i >= argc)
- die("-n requires an argument");
- revs.max_count = atoi(argv[i]);
- continue;
- }
- if (!strncmp(arg,"-n",2)) {
- revs.max_count = atoi(arg + 2);
- continue;
- }
if (!strcmp(arg, "--header")) {
- verbose_header = 1;
- continue;
- }
- if (!strcmp(arg, "--no-abbrev")) {
- abbrev = 0;
- continue;
- }
- if (!strcmp(arg, "--abbrev")) {
- abbrev = DEFAULT_ABBREV;
- continue;
- }
- if (!strcmp(arg, "--abbrev-commit")) {
- abbrev_commit = 1;
- continue;
- }
- if (!strncmp(arg, "--abbrev=", 9)) {
- abbrev = strtoul(arg + 9, NULL, 10);
- if (abbrev && abbrev < MINIMUM_ABBREV)
- abbrev = MINIMUM_ABBREV;
- else if (40 < abbrev)
- abbrev = 40;
- continue;
- }
- if (!strncmp(arg, "--pretty", 8)) {
- commit_format = get_commit_format(arg+8);
- verbose_header = 1;
- hdr_termination = '\n';
- if (commit_format == CMIT_FMT_ONELINE)
- commit_prefix = "";
- else
- commit_prefix = "commit ";
+ revs.verbose_header = 1;
continue;
}
if (!strcmp(arg, "--timestamp")) {
@@ -362,14 +319,27 @@ int main(int argc, const char **argv)
usage(rev_list_usage);
}
+ if (revs.commit_format != CMIT_FMT_UNSPECIFIED) {
+ /* The command line has a --pretty */
+ hdr_termination = '\n';
+ if (revs.commit_format == CMIT_FMT_ONELINE)
+ header_prefix = "";
+ else
+ header_prefix = "commit ";
+ }
+ else if (revs.verbose_header)
+ /* Only --header was specified */
+ revs.commit_format = CMIT_FMT_RAW;
list = revs.commits;
- if (!list &&
- (!(revs.tag_objects||revs.tree_objects||revs.blob_objects) && !revs.pending_objects))
+ if ((!list &&
+ (!(revs.tag_objects||revs.tree_objects||revs.blob_objects) &&
+ !revs.pending_objects)) ||
+ revs.diff)
usage(rev_list_usage);
- save_commit_buffer = verbose_header;
+ save_commit_buffer = revs.verbose_header;
track_object_refs = 0;
if (bisect_list)
revs.limited = 1;
diff --git a/revision.c b/revision.c
index 03dd23893..f2a9f25fe 100644
--- a/revision.c
+++ b/revision.c
@@ -116,21 +116,27 @@ static void add_pending_object(struct rev_info *revs, struct object *obj, const
add_object(obj, &revs->pending_objects, NULL, name);
}
-static struct commit *get_commit_reference(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags)
+static struct object *get_reference(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags)
{
struct object *object;
object = parse_object(sha1);
if (!object)
die("bad object %s", name);
+ object->flags |= flags;
+ return object;
+}
+
+static struct commit *handle_commit(struct rev_info *revs, struct object *object, const char *name)
+{
+ unsigned long flags = object->flags;
/*
* Tag object? Look what it points to..
*/
while (object->type == tag_type) {
struct tag *tag = (struct tag *) object;
- object->flags |= flags;
- if (revs->tag_objects && !(object->flags & UNINTERESTING))
+ if (revs->tag_objects && !(flags & UNINTERESTING))
add_pending_object(revs, object, tag->tag);
object = parse_object(tag->tagged->sha1);
if (!object)
@@ -143,10 +149,10 @@ static struct commit *get_commit_reference(struct rev_info *revs, const char *na
*/
if (object->type == commit_type) {
struct commit *commit = (struct commit *)object;
- object->flags |= flags;
if (parse_commit(commit) < 0)
die("unable to parse commit %s", name);
if (flags & UNINTERESTING) {
+ commit->object.flags |= UNINTERESTING;
mark_parents_uninteresting(commit);
revs->limited = 1;
}
@@ -241,7 +247,7 @@ int rev_compare_tree(struct rev_info *revs, struct tree *t1, struct tree *t2)
return REV_TREE_DIFFERENT;
tree_difference = REV_TREE_SAME;
if (diff_tree_sha1(t1->object.sha1, t2->object.sha1, "",
- &revs->diffopt) < 0)
+ &revs->pruning) < 0)
return REV_TREE_DIFFERENT;
return tree_difference;
}
@@ -264,7 +270,7 @@ int rev_same_tree_as_empty(struct rev_info *revs, struct tree *t1)
empty.size = 0;
tree_difference = 0;
- retval = diff_tree(&empty, &real, "", &revs->diffopt);
+ retval = diff_tree(&empty, &real, "", &revs->pruning);
free(tree);
return retval >= 0 && !tree_difference;
@@ -375,6 +381,9 @@ static void add_parents_to_list(struct rev_info *revs, struct commit *commit, st
if (revs->prune_fn)
revs->prune_fn(revs, commit);
+ if (revs->no_walk)
+ return;
+
parent = commit->parents;
while (parent) {
struct commit *p = parent->item;
@@ -451,21 +460,13 @@ static void limit_list(struct rev_info *revs)
revs->commits = newlist;
}
-static void add_one_commit(struct commit *commit, struct rev_info *revs)
-{
- if (!commit || (commit->object.flags & SEEN))
- return;
- commit->object.flags |= SEEN;
- commit_list_insert(commit, &revs->commits);
-}
-
static int all_flags;
static struct rev_info *all_revs;
static int handle_one_ref(const char *path, const unsigned char *sha1)
{
- struct commit *commit = get_commit_reference(all_revs, path, sha1, all_flags);
- add_one_commit(commit, all_revs);
+ struct object *object = get_reference(all_revs, path, sha1, all_flags);
+ add_pending_object(all_revs, object, "");
return 0;
}
@@ -479,9 +480,12 @@ static void handle_all(struct rev_info *revs, unsigned flags)
void init_revisions(struct rev_info *revs)
{
memset(revs, 0, sizeof(*revs));
- revs->diffopt.recursive = 1;
- revs->diffopt.add_remove = file_add_remove;
- revs->diffopt.change = file_change;
+
+ revs->abbrev = DEFAULT_ABBREV;
+ revs->ignore_merges = 1;
+ revs->pruning.recursive = 1;
+ revs->pruning.add_remove = file_add_remove;
+ revs->pruning.change = file_change;
revs->lifo = 1;
revs->dense = 1;
revs->prefix = setup_git_directory();
@@ -494,6 +498,10 @@ void init_revisions(struct rev_info *revs)
revs->topo_setter = topo_sort_default_setter;
revs->topo_getter = topo_sort_default_getter;
+
+ revs->commit_format = CMIT_FMT_DEFAULT;
+
+ diff_setup(&revs->diffopt);
}
/*
@@ -509,8 +517,6 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
const char **unrecognized = argv + 1;
int left = 1;
- init_revisions(revs);
-
/* First, search for "--" */
seen_dashdash = 0;
for (i = 1; i < argc; i++) {
@@ -526,13 +532,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
flags = 0;
for (i = 1; i < argc; i++) {
- struct commit *commit;
+ struct object *object;
const char *arg = argv[i];
unsigned char sha1[20];
char *dotdot;
int local_flags;
if (*arg == '-') {
+ int opts;
if (!strncmp(arg, "--max-count=", 12)) {
revs->max_count = atoi(arg + 12);
continue;
@@ -640,6 +647,76 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
revs->unpacked = 1;
continue;
}
+ if (!strcmp(arg, "-r")) {
+ revs->diff = 1;
+ revs->diffopt.recursive = 1;
+ continue;
+ }
+ if (!strcmp(arg, "-t")) {
+ revs->diff = 1;
+ revs->diffopt.recursive = 1;
+ revs->diffopt.tree_in_recursive = 1;
+ continue;
+ }
+ if (!strcmp(arg, "-m")) {
+ revs->ignore_merges = 0;
+ continue;
+ }
+ if (!strcmp(arg, "-c")) {
+ revs->diff = 1;
+ revs->combine_merges = 1;
+ continue;
+ }
+ if (!strcmp(arg, "--cc")) {
+ revs->diff = 1;
+ revs->dense_combined_merges = 1;
+ revs->combine_merges = 1;
+ continue;
+ }
+ if (!strcmp(arg, "-v")) {
+ revs->verbose_header = 1;
+ continue;
+ }
+ if (!strncmp(arg, "--pretty", 8)) {
+ revs->verbose_header = 1;
+ revs->commit_format = get_commit_format(arg+8);
+ continue;
+ }
+ if (!strcmp(arg, "--root")) {
+ revs->show_root_diff = 1;
+ continue;
+ }
+ if (!strcmp(arg, "--no-commit-id")) {
+ revs->no_commit_id = 1;
+ continue;
+ }
+ if (!strcmp(arg, "--always")) {
+ revs->always_show_header = 1;
+ continue;
+ }
+ if (!strcmp(arg, "--no-abbrev")) {
+ revs->abbrev = 0;
+ continue;
+ }
+ if (!strcmp(arg, "--abbrev")) {
+ revs->abbrev = DEFAULT_ABBREV;
+ continue;
+ }
+ if (!strcmp(arg, "--abbrev-commit")) {
+ revs->abbrev_commit = 1;
+ continue;
+ }
+ if (!strcmp(arg, "--full-diff")) {
+ revs->diff = 1;
+ revs->full_diff = 1;
+ continue;
+ }
+ opts = diff_opt_parse(&revs->diffopt, argv+i, argc-i);
+ if (opts > 0) {
+ revs->diff = 1;
+ i += opts - 1;
+ continue;
+ }
*unrecognized++ = arg;
left++;
continue;
@@ -656,15 +733,15 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
this = "HEAD";
if (!get_sha1(this, from_sha1) &&
!get_sha1(next, sha1)) {
- struct commit *exclude;
- struct commit *include;
+ struct object *exclude;
+ struct object *include;
- exclude = get_commit_reference(revs, this, from_sha1, flags ^ UNINTERESTING);
- include = get_commit_reference(revs, next, sha1, flags);
+ exclude = get_reference(revs, this, from_sha1, flags ^ UNINTERESTING);
+ include = get_reference(revs, next, sha1, flags);
if (!exclude || !include)
die("Invalid revision range %s..%s", arg, next);
- add_one_commit(exclude, revs);
- add_one_commit(include, revs);
+ add_pending_object(revs, exclude, this);
+ add_pending_object(revs, include, next);
continue;
}
*dotdot = '.';
@@ -687,32 +764,58 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
revs->prune_data = get_pathspec(revs->prefix, argv + i);
break;
}
- commit = get_commit_reference(revs, arg, sha1, flags ^ local_flags);
- add_one_commit(commit, revs);
+ object = get_reference(revs, arg, sha1, flags ^ local_flags);
+ add_pending_object(revs, object, arg);
}
- if (def && !revs->commits) {
+ if (def && !revs->pending_objects) {
unsigned char sha1[20];
- struct commit *commit;
+ struct object *object;
if (get_sha1(def, sha1) < 0)
die("bad default revision '%s'", def);
- commit = get_commit_reference(revs, def, sha1, 0);
- add_one_commit(commit, revs);
+ object = get_reference(revs, def, sha1, 0);
+ add_pending_object(revs, object, def);
}
if (revs->topo_order || revs->unpacked)
revs->limited = 1;
if (revs->prune_data) {
- diff_tree_setup_paths(revs->prune_data, &revs->diffopt);
+ diff_tree_setup_paths(revs->prune_data, &revs->pruning);
revs->prune_fn = try_to_simplify_commit;
+ if (!revs->full_diff)
+ diff_tree_setup_paths(revs->prune_data, &revs->diffopt);
}
+ if (revs->combine_merges) {
+ revs->ignore_merges = 0;
+ if (revs->dense_combined_merges &&
+ (revs->diffopt.output_format != DIFF_FORMAT_DIFFSTAT))
+ revs->diffopt.output_format = DIFF_FORMAT_PATCH;
+ }
+ revs->diffopt.abbrev = revs->abbrev;
+ diff_setup_done(&revs->diffopt);
return left;
}
void prepare_revision_walk(struct rev_info *revs)
{
- sort_by_date(&revs->commits);
+ struct object_list *list;
+
+ list = revs->pending_objects;
+ revs->pending_objects = NULL;
+ while (list) {
+ struct commit *commit = handle_commit(revs, list->item, list->name);
+ if (commit) {
+ if (!(commit->object.flags & SEEN)) {
+ commit->object.flags |= SEEN;
+ insert_by_date(commit, &revs->commits);
+ }
+ }
+ list = list->next;
+ }
+
+ if (revs->no_walk)
+ return;
if (revs->limited)
limit_list(revs);
if (revs->topo_order)
diff --git a/revision.h b/revision.h
index 4b2704351..48d7b4ca9 100644
--- a/revision.h
+++ b/revision.h
@@ -11,6 +11,7 @@
#define ADDED (1u<<7) /* Parents already parsed and added? */
struct rev_info;
+struct log_info;
typedef void (prune_fn_t)(struct rev_info *revs, struct commit *commit);
@@ -27,6 +28,7 @@ struct rev_info {
/* Traversal flags */
unsigned int dense:1,
no_merges:1,
+ no_walk:1,
remove_empty_trees:1,
lifo:1,
topo_order:1,
@@ -39,13 +41,32 @@ struct rev_info {
boundary:1,
parents:1;
+ /* Diff flags */
+ unsigned int diff:1,
+ full_diff:1,
+ show_root_diff:1,
+ no_commit_id:1,
+ verbose_header:1,
+ ignore_merges:1,
+ combine_merges:1,
+ dense_combined_merges:1,
+ always_show_header:1;
+
+ /* Format info */
+ unsigned int shown_one:1,
+ abbrev_commit:1;
+ unsigned int abbrev;
+ enum cmit_fmt commit_format;
+ struct log_info *loginfo;
+
/* special limits */
int max_count;
unsigned long max_age;
unsigned long min_age;
- /* paths limiting */
+ /* diff info for patches and for paths limiting */
struct diff_options diffopt;
+ struct diff_options pruning;
topo_sort_set_fn_t topo_setter;
topo_sort_get_fn_t topo_getter;
diff --git a/sha1_name.c b/sha1_name.c
index 4f92e12a8..345935bb2 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -3,6 +3,7 @@
#include "commit.h"
#include "tree.h"
#include "blob.h"
+#include "tree-walk.h"
static int find_short_object_filename(int len, const char *name, unsigned char *sha1)
{
@@ -455,6 +456,19 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1)
*/
int get_sha1(const char *name, unsigned char *sha1)
{
+ int ret;
+ unsigned unused;
+
prepare_alt_odb();
- return get_sha1_1(name, strlen(name), sha1);
+ ret = get_sha1_1(name, strlen(name), sha1);
+ if (ret < 0) {
+ const char *cp = strchr(name, ':');
+ if (cp) {
+ unsigned char tree_sha1[20];
+ if (!get_sha1_1(name, cp-name, tree_sha1))
+ return get_tree_entry(tree_sha1, cp+1, sha1,
+ &unused);
+ }
+ }
+ return ret;
}
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index 6729a1826..c69706fb3 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -174,6 +174,27 @@ test_expect_success \
'git-ls-tree -r output for a known tree.' \
'diff current expected'
+# But with -r -t we can have both.
+test_expect_success \
+ 'showing tree with git-ls-tree -r -t' \
+ 'git-ls-tree -r -t $tree >current'
+cat >expected <<\EOF
+100644 blob f87290f8eb2cbbea7857214459a0739927eab154 path0
+120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01 path0sym
+040000 tree 58a09c23e2ca152193f2786e06986b7b6712bdbe path2
+100644 blob 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 path2/file2
+120000 blob d8ce161addc5173867a3c3c730924388daedbc38 path2/file2sym
+040000 tree 21ae8269cacbe57ae09138dcc3a2887f904d02b3 path3
+100644 blob 0aa34cae68d0878578ad119c86ca2b5ed5b28376 path3/file3
+120000 blob 8599103969b43aff7e430efea79ca4636466794f path3/file3sym
+040000 tree 3c5e5399f3a333eddecce7a9b9465b63f65f51e2 path3/subp3
+100644 blob 00fb5908cb97c2564a9783c0c64087333b3b464f path3/subp3/file3
+120000 blob 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c path3/subp3/file3sym
+EOF
+test_expect_success \
+ 'git-ls-tree -r output for a known tree.' \
+ 'diff current expected'
+
################################################################
rm .git/index
test_expect_success \
diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh
index d0ed24275..75e4c9a88 100755
--- a/t/t1001-read-tree-m-2way.sh
+++ b/t/t1001-read-tree-m-2way.sh
@@ -37,7 +37,7 @@ compare_change () {
}
check_cache_at () {
- clean_if_empty=`git-diff-files "$1"`
+ clean_if_empty=`git-diff-files -- "$1"`
case "$clean_if_empty" in
'') echo "$1: clean" ;;
?*) echo "$1: dirty" ;;
diff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh
index 861ef4c0c..4d175d8ea 100755
--- a/t/t1002-read-tree-m-u-2way.sh
+++ b/t/t1002-read-tree-m-u-2way.sh
@@ -20,7 +20,7 @@ compare_change () {
}
check_cache_at () {
- clean_if_empty=`git-diff-files "$1"`
+ clean_if_empty=`git-diff-files -- "$1"`
case "$clean_if_empty" in
'') echo "$1: clean" ;;
?*) echo "$1: dirty" ;;
diff --git a/t/t1200-tutorial.sh b/t/t1200-tutorial.sh
index f4d53c078..c7db20e7f 100755
--- a/t/t1200-tutorial.sh
+++ b/t/t1200-tutorial.sh
@@ -49,7 +49,7 @@ test_expect_success 'git diff HEAD' 'cmp diff.expect diff.output'
#test_expect_success 'git-read-tree --reset HEAD' "git-read-tree --reset HEAD ; test \"hello: needs update\" = \"$(git-update-index --refresh)\""
cat > whatchanged.expect << EOF
-diff-tree VARIABLE (from root)
+commit VARIABLE
Author: VARIABLE
Date: VARIABLE
@@ -72,7 +72,7 @@ index 0000000..557db03
EOF
git-whatchanged -p --root | \
- sed -e "1s/^\(.\{10\}\).\{40\}/\1VARIABLE/" \
+ sed -e "1s/^\(.\{7\}\).\{40\}/\1VARIABLE/" \
-e "2,3s/^\(.\{8\}\).*$/\1VARIABLE/" \
> whatchanged.output
test_expect_success 'git-whatchanged -p --root' 'cmp whatchanged.expect whatchanged.output'
diff --git a/t/t4010-diff-pathspec.sh b/t/t4010-diff-pathspec.sh
index 8db329d7f..9e1544df9 100755
--- a/t/t4010-diff-pathspec.sh
+++ b/t/t4010-diff-pathspec.sh
@@ -28,7 +28,7 @@ cat >expected <<\EOF
EOF
test_expect_success \
'limit to path should show nothing' \
- 'git-diff-index --cached $tree path >current &&
+ 'git-diff-index --cached $tree -- path >current &&
compare_diff_raw current expected'
cat >expected <<\EOF
@@ -36,7 +36,7 @@ cat >expected <<\EOF
EOF
test_expect_success \
'limit to path1 should show path1/file1' \
- 'git-diff-index --cached $tree path1 >current &&
+ 'git-diff-index --cached $tree -- path1 >current &&
compare_diff_raw current expected'
cat >expected <<\EOF
@@ -44,7 +44,7 @@ cat >expected <<\EOF
EOF
test_expect_success \
'limit to path1/ should show path1/file1' \
- 'git-diff-index --cached $tree path1/ >current &&
+ 'git-diff-index --cached $tree -- path1/ >current &&
compare_diff_raw current expected'
cat >expected <<\EOF
@@ -52,14 +52,14 @@ cat >expected <<\EOF
EOF
test_expect_success \
'limit to file0 should show file0' \
- 'git-diff-index --cached $tree file0 >current &&
+ 'git-diff-index --cached $tree -- file0 >current &&
compare_diff_raw current expected'
cat >expected <<\EOF
EOF
test_expect_success \
'limit to file0/ should emit nothing.' \
- 'git-diff-index --cached $tree file0/ >current &&
+ 'git-diff-index --cached $tree -- file0/ >current &&
compare_diff_raw current expected'
test_done
diff --git a/tree-walk.c b/tree-walk.c
index bf8bfdfdf..9f7abb7cb 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -115,3 +115,53 @@ void traverse_trees(int n, struct tree_desc *t, const char *base, traverse_callb
free(entry);
}
+static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char *result, unsigned *mode)
+{
+ int namelen = strlen(name);
+ while (t->size) {
+ const char *entry;
+ const unsigned char *sha1;
+ int entrylen, cmp;
+
+ sha1 = tree_entry_extract(t, &entry, mode);
+ update_tree_entry(t);
+ entrylen = strlen(entry);
+ if (entrylen > namelen)
+ continue;
+ cmp = memcmp(name, entry, entrylen);
+ if (cmp > 0)
+ continue;
+ if (cmp < 0)
+ break;
+ if (entrylen == namelen) {
+ memcpy(result, sha1, 20);
+ return 0;
+ }
+ if (name[entrylen] != '/')
+ continue;
+ if (!S_ISDIR(*mode))
+ break;
+ if (++entrylen == namelen) {
+ memcpy(result, sha1, 20);
+ return 0;
+ }
+ return get_tree_entry(sha1, name + entrylen, result, mode);
+ }
+ return -1;
+}
+
+int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned char *sha1, unsigned *mode)
+{
+ int retval;
+ void *tree;
+ struct tree_desc t;
+
+ tree = read_object_with_reference(tree_sha1, tree_type, &t.size, NULL);
+ if (!tree)
+ return -1;
+ t.buf = tree;
+ retval = find_tree_entry(&t, name, sha1, mode);
+ free(tree);
+ return retval;
+}
+
diff --git a/tree-walk.h b/tree-walk.h
index 76893e36c..47438fe1c 100644
--- a/tree-walk.h
+++ b/tree-walk.h
@@ -22,4 +22,6 @@ typedef void (*traverse_callback_t)(int n, unsigned long mask, struct name_entry
void traverse_trees(int n, struct tree_desc *t, const char *base, traverse_callback_t callback);
+int get_tree_entry(const unsigned char *, const char *, unsigned char *, unsigned *);
+
#endif
diff --git a/update-index.c b/update-index.c
index 1efac27c6..facec8d91 100644
--- a/update-index.c
+++ b/update-index.c
@@ -6,6 +6,7 @@
#include "cache.h"
#include "strbuf.h"
#include "quote.h"
+#include "tree-walk.h"
/*
* Default to not allowing changes to the list of files. The
@@ -328,7 +329,7 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
return 0;
}
-static int chmod_path(int flip, const char *path)
+static void chmod_path(int flip, const char *path)
{
int pos;
struct cache_entry *ce;
@@ -336,21 +337,24 @@ static int chmod_path(int flip, const char *path)
pos = cache_name_pos(path, strlen(path));
if (pos < 0)
- return -1;
+ goto fail;
ce = active_cache[pos];
mode = ntohl(ce->ce_mode);
if (!S_ISREG(mode))
- return -1;
+ goto fail;
switch (flip) {
case '+':
ce->ce_mode |= htonl(0111); break;
case '-':
ce->ce_mode &= htonl(~0111); break;
default:
- return -1;
+ goto fail;
}
active_cache_changed = 1;
- return 0;
+ report("chmod %cx '%s'", flip, path);
+ return;
+ fail:
+ die("git-update-index: cannot chmod %cx '%s'", flip, path);
}
static struct cache_file cache_file;
@@ -471,6 +475,124 @@ static void read_index_info(int line_termination)
static const char update_index_usage[] =
"git-update-index [-q] [--add] [--replace] [--remove] [--unmerged] [--refresh] [--cacheinfo] [--chmod=(+|-)x] [--info-only] [--force-remove] [--stdin] [--index-info] [--ignore-missing] [-z] [--verbose] [--] <file>...";
+static unsigned char head_sha1[20];
+static unsigned char merge_head_sha1[20];
+
+static struct cache_entry *read_one_ent(const char *which,
+ unsigned char *ent, const char *path,
+ int namelen, int stage)
+{
+ unsigned mode;
+ unsigned char sha1[20];
+ int size;
+ struct cache_entry *ce;
+
+ if (get_tree_entry(ent, path, sha1, &mode)) {
+ error("%s: not in %s branch.", path, which);
+ return NULL;
+ }
+ if (mode == S_IFDIR) {
+ error("%s: not a blob in %s branch.", path, which);
+ return NULL;
+ }
+ size = cache_entry_size(namelen);
+ ce = xcalloc(1, size);
+
+ memcpy(ce->sha1, sha1, 20);
+ memcpy(ce->name, path, namelen);
+ ce->ce_flags = create_ce_flags(namelen, stage);
+ ce->ce_mode = create_ce_mode(mode);
+ return ce;
+}
+
+static int unresolve_one(const char *path)
+{
+ int namelen = strlen(path);
+ int pos;
+ int ret = 0;
+ struct cache_entry *ce_2 = NULL, *ce_3 = NULL;
+
+ /* See if there is such entry in the index. */
+ pos = cache_name_pos(path, namelen);
+ if (pos < 0) {
+ /* If there isn't, either it is unmerged, or
+ * resolved as "removed" by mistake. We do not
+ * want to do anything in the former case.
+ */
+ pos = -pos-1;
+ if (pos < active_nr) {
+ struct cache_entry *ce = active_cache[pos];
+ if (ce_namelen(ce) == namelen &&
+ !memcmp(ce->name, path, namelen)) {
+ fprintf(stderr,
+ "%s: skipping still unmerged path.\n",
+ path);
+ goto free_return;
+ }
+ }
+ }
+
+ /* Grab blobs from given path from HEAD and MERGE_HEAD,
+ * stuff HEAD version in stage #2,
+ * stuff MERGE_HEAD version in stage #3.
+ */
+ ce_2 = read_one_ent("our", head_sha1, path, namelen, 2);
+ ce_3 = read_one_ent("their", merge_head_sha1, path, namelen, 3);
+
+ if (!ce_2 || !ce_3) {
+ ret = -1;
+ goto free_return;
+ }
+ if (!memcmp(ce_2->sha1, ce_3->sha1, 20) &&
+ ce_2->ce_mode == ce_3->ce_mode) {
+ fprintf(stderr, "%s: identical in both, skipping.\n",
+ path);
+ goto free_return;
+ }
+
+ remove_file_from_cache(path);
+ if (add_cache_entry(ce_2, ADD_CACHE_OK_TO_ADD)) {
+ error("%s: cannot add our version to the index.", path);
+ ret = -1;
+ goto free_return;
+ }
+ if (!add_cache_entry(ce_3, ADD_CACHE_OK_TO_ADD))
+ return 0;
+ error("%s: cannot add their version to the index.", path);
+ ret = -1;
+ free_return:
+ free(ce_2);
+ free(ce_3);
+ return ret;
+}
+
+static void read_head_pointers(void)
+{
+ if (read_ref(git_path("HEAD"), head_sha1))
+ die("No HEAD -- no initial commit yet?\n");
+ if (read_ref(git_path("MERGE_HEAD"), merge_head_sha1)) {
+ fprintf(stderr, "Not in the middle of a merge.\n");
+ exit(0);
+ }
+}
+
+static int do_unresolve(int ac, const char **av)
+{
+ int i;
+ int err = 0;
+
+ /* Read HEAD and MERGE_HEAD; if MERGE_HEAD does not exist, we
+ * are not doing a merge, so exit with success status.
+ */
+ read_head_pointers();
+
+ for (i = 1; i < ac; i++) {
+ const char *arg = av[i];
+ err |= unresolve_one(arg);
+ }
+ return err;
+}
+
int main(int argc, const char **argv)
{
int i, newfd, entries, has_errors = 0, line_termination = '\n';
@@ -478,6 +600,7 @@ int main(int argc, const char **argv)
int read_from_stdin = 0;
const char *prefix = setup_git_directory();
int prefix_length = prefix ? strlen(prefix) : 0;
+ char set_executable_bit = 0;
git_config(git_default_config);
@@ -544,8 +667,7 @@ int main(int argc, const char **argv)
!strcmp(path, "--chmod=+x")) {
if (argc <= i+1)
die("git-update-index: %s <path>", path);
- if (chmod_path(path[8], argv[++i]))
- die("git-update-index: %s cannot chmod %s", path, argv[i]);
+ set_executable_bit = path[8];
continue;
}
if (!strcmp(path, "--assume-unchanged")) {
@@ -581,6 +703,12 @@ int main(int argc, const char **argv)
read_index_info(line_termination);
break;
}
+ if (!strcmp(path, "--unresolve")) {
+ has_errors = do_unresolve(argc - i, argv + i);
+ if (has_errors)
+ active_cache_changed = 0;
+ goto finish;
+ }
if (!strcmp(path, "--ignore-missing")) {
not_new = 1;
continue;
@@ -594,6 +722,8 @@ int main(int argc, const char **argv)
die("unknown option %s", path);
}
update_one(path, prefix, prefix_length);
+ if (set_executable_bit)
+ chmod_path(set_executable_bit, path);
}
if (read_from_stdin) {
struct strbuf buf;
@@ -608,10 +738,16 @@ int main(int argc, const char **argv)
else
path_name = buf.buf;
update_one(path_name, prefix, prefix_length);
+ if (set_executable_bit) {
+ const char *p = prefix_path(prefix, prefix_length, path_name);
+ chmod_path(set_executable_bit, p);
+ }
if (path_name != buf.buf)
free(path_name);
}
}
+
+ finish:
if (active_cache_changed) {
if (write_cache(newfd, active_cache, active_nr) ||
commit_index_file(&cache_file))