Browse Source

Merge branch 'master' into lt/apply

* master: (40 commits)
  Clean up sha1 file writing
  Builtin git-cat-file
  builtin format-patch: squelch content-type for 7-bit ASCII
  CMIT_FMT_EMAIL: Q-encode Subject: and display-name part of From: fields.
  add more informative error messages to git-mktag
  remove the artificial restriction tagsize < 8kb
  git-rebase: use canonical A..B syntax to format-patch
  git-format-patch: now built-in.
  fmt-patch: Support --attach
  fmt-patch: understand old <his> notation
  Teach fmt-patch about --keep-subject
  Teach fmt-patch about --numbered
  fmt-patch: implement -o <dir>
  fmt-patch: output file names to stdout
  Teach fmt-patch to write individual files.
  Use RFC2822 dates from "git fmt-patch".
  git-fmt-patch: thinkofix to show [PATCH] properly.
  rename internal format-patch wip
  Minor tweak on subject line in --pretty=email
  Tentative built-in format-patch.
  ...
maint
Junio C Hamano 19 years ago
parent
commit
4868f3729a
  1. 44
      Makefile
  2. 187
      builtin-add.c
  3. 3
      builtin-apply.c
  4. 3
      builtin-cat-file.c
  5. 5
      builtin-commit-tree.c
  6. 3
      builtin-diff-files.c
  7. 3
      builtin-diff-index.c
  8. 3
      builtin-diff-stages.c
  9. 3
      builtin-diff-tree.c
  10. 2
      builtin-diff.c
  11. 172
      builtin-log.c
  12. 384
      builtin-ls-files.c
  13. 5
      builtin-ls-tree.c
  14. 3
      builtin-read-tree.c
  15. 2
      builtin-rev-list.c
  16. 150
      builtin-rm.c
  17. 9
      builtin-show-branch.c
  18. 62
      builtin-tar-tree.c
  19. 74
      builtin-upload-tar.c
  20. 16
      builtin.h
  21. 3
      cache.h
  22. 134
      commit.c
  23. 3
      commit.h
  24. 2
      connect.c
  25. 29
      date.c
  26. 11
      diff.c
  27. 3
      diff.h
  28. 401
      dir.c
  29. 51
      dir.h
  30. 66
      fetch-pack.c
  31. 56
      git-add.sh
  32. 344
      git-format-patch.sh
  33. 2
      git-rebase.sh
  34. 70
      git-rm.sh
  35. 19
      git.c
  36. 74
      log-tree.c
  37. 45
      mktag.c
  38. 66
      read-cache.c
  39. 2
      revision.h
  40. 185
      sha1_file.c
  41. 64
      update-index.c

44
Makefile

@ -113,14 +113,14 @@ SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__ @@ -113,14 +113,14 @@ SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__
### --- END CONFIGURATION SECTION ---

SCRIPT_SH = \
git-add.sh git-bisect.sh git-branch.sh git-checkout.sh \
git-bisect.sh git-branch.sh git-checkout.sh \
git-cherry.sh git-clean.sh git-clone.sh git-commit.sh \
git-fetch.sh \
git-format-patch.sh git-ls-remote.sh \
git-ls-remote.sh \
git-merge-one-file.sh git-parse-remote.sh \
git-prune.sh git-pull.sh git-rebase.sh \
git-repack.sh git-request-pull.sh git-reset.sh \
git-resolve.sh git-revert.sh git-rm.sh git-sh-setup.sh \
git-resolve.sh git-revert.sh git-sh-setup.sh \
git-tag.sh git-verify-tag.sh \
git-applymbox.sh git-applypatch.sh git-am.sh \
git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
@ -149,19 +149,16 @@ SIMPLE_PROGRAMS = \ @@ -149,19 +149,16 @@ SIMPLE_PROGRAMS = \

# ... and all the rest that could be moved out of bindir to gitexecdir
PROGRAMS = \
git-apply$X git-cat-file$X \
git-checkout-index$X git-clone-pack$X git-commit-tree$X \
git-convert-objects$X git-diff-files$X \
git-diff-index$X git-diff-stages$X \
git-diff-tree$X git-fetch-pack$X git-fsck-objects$X \
git-checkout-index$X git-clone-pack$X \
git-convert-objects$X git-fetch-pack$X git-fsck-objects$X \
git-hash-object$X git-index-pack$X git-local-fetch$X \
git-ls-files$X git-ls-tree$X git-mailinfo$X git-merge-base$X \
git-mailinfo$X git-merge-base$X \
git-merge-index$X git-mktag$X git-mktree$X git-pack-objects$X git-patch-id$X \
git-peek-remote$X git-prune-packed$X git-read-tree$X \
git-peek-remote$X git-prune-packed$X \
git-receive-pack$X git-rev-parse$X \
git-send-pack$X git-show-branch$X git-shell$X \
git-send-pack$X git-shell$X \
git-show-index$X git-ssh-fetch$X \
git-ssh-upload$X git-tar-tree$X git-unpack-file$X \
git-ssh-upload$X git-unpack-file$X \
git-unpack-objects$X git-update-index$X git-update-server-info$X \
git-upload-pack$X git-verify-pack$X git-write-tree$X \
git-update-ref$X git-symbolic-ref$X \
@ -170,8 +167,13 @@ PROGRAMS = \ @@ -170,8 +167,13 @@ PROGRAMS = \

BUILT_INS = git-log$X git-whatchanged$X git-show$X \
git-count-objects$X git-diff$X git-push$X \
git-grep$X git-rev-list$X git-check-ref-format$X \
git-init-db$X
git-grep$X git-add$X git-rm$X git-rev-list$X \
git-check-ref-format$X \
git-init-db$X git-tar-tree$X git-upload-tar$X git-format-patch$X \
git-ls-files$X git-ls-tree$X \
git-read-tree$X git-commit-tree$X \
git-apply$X git-show-branch$X git-diff-files$X \
git-diff-index$X git-diff-stages$X git-diff-tree$X git-cat-file$X

# what 'all' will build and 'install' will install, in gitexecdir
ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS)
@ -200,7 +202,7 @@ LIB_H = \ @@ -200,7 +202,7 @@ LIB_H = \
blob.h cache.h commit.h csum-file.h delta.h \
diff.h object.h pack.h pkt-line.h quote.h refs.h \
run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
tree-walk.h log-tree.h
tree-walk.h log-tree.h dir.h

DIFF_OBJS = \
diff.o diff-lib.o diffcore-break.o diffcore-order.o \
@ -211,7 +213,7 @@ LIB_OBJS = \ @@ -211,7 +213,7 @@ LIB_OBJS = \
blob.o commit.o connect.o csum-file.o base85.o \
date.o diff-delta.o entry.o exec_cmd.o ident.o index.o \
object.o pack-check.o patch-delta.o path.o pkt-line.o \
quote.o read-cache.o refs.o run-command.o \
quote.o read-cache.o refs.o run-command.o dir.o \
server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \
@ -219,8 +221,14 @@ LIB_OBJS = \ @@ -219,8 +221,14 @@ LIB_OBJS = \

BUILTIN_OBJS = \
builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \
builtin-grep.o builtin-rev-list.o builtin-check-ref-format.o \
builtin-init-db.o
builtin-grep.o builtin-add.o builtin-rev-list.o builtin-check-ref-format.o \
builtin-rm.o builtin-init-db.o \
builtin-tar-tree.o builtin-upload-tar.o \
builtin-ls-files.o builtin-ls-tree.o \
builtin-read-tree.o builtin-commit-tree.o \
builtin-apply.o builtin-show-branch.o builtin-diff-files.o \
builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o \
builtin-cat-file.o

GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
LIBS = $(GITLIBS) -lz

187
builtin-add.c

@ -0,0 +1,187 @@ @@ -0,0 +1,187 @@
/*
* "git add" builtin command
*
* Copyright (C) 2006 Linus Torvalds
*/
#include <fnmatch.h>

#include "cache.h"
#include "builtin.h"
#include "dir.h"

static const char builtin_add_usage[] =
"git-add [-n] [-v] <filepattern>...";

static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
{
char *seen;
int i, specs;
struct dir_entry **src, **dst;

for (specs = 0; pathspec[specs]; specs++)
/* nothing */;
seen = xmalloc(specs);
memset(seen, 0, specs);

src = dst = dir->entries;
i = dir->nr;
while (--i >= 0) {
struct dir_entry *entry = *src++;
if (!match_pathspec(pathspec, entry->name, entry->len, prefix, seen)) {
free(entry);
continue;
}
*dst++ = entry;
}
dir->nr = dst - dir->entries;

for (i = 0; i < specs; i++) {
struct stat st;
const char *match;
if (seen[i])
continue;

/* Existing file? We must have ignored it */
match = pathspec[i];
if (!match[0] || !lstat(match, &st))
continue;
die("pathspec '%s' did not match any files", match);
}
}

static void fill_directory(struct dir_struct *dir, const char **pathspec)
{
const char *path, *base;
int baselen;

/* Set up the default git porcelain excludes */
memset(dir, 0, sizeof(*dir));
dir->exclude_per_dir = ".gitignore";
path = git_path("info/exclude");
if (!access(path, R_OK))
add_excludes_from_file(dir, path);

/*
* Calculate common prefix for the pathspec, and
* use that to optimize the directory walk
*/
baselen = common_prefix(pathspec);
path = ".";
base = "";
if (baselen) {
char *common = xmalloc(baselen + 1);
common = xmalloc(baselen + 1);
memcpy(common, *pathspec, baselen);
common[baselen] = 0;
path = base = common;
}

/* Read the directory and prune it */
read_directory(dir, path, base, baselen);
if (pathspec)
prune_directory(dir, pathspec, baselen);
}

static int add_file_to_index(const char *path, int verbose)
{
int size, namelen;
struct stat st;
struct cache_entry *ce;

if (lstat(path, &st))
die("%s: unable to stat (%s)", path, strerror(errno));

if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))
die("%s: can only add regular files or symbolic links", path);

namelen = strlen(path);
size = cache_entry_size(namelen);
ce = xcalloc(1, size);
memcpy(ce->name, path, namelen);
ce->ce_flags = htons(namelen);
fill_stat_cache_info(ce, &st);

ce->ce_mode = create_ce_mode(st.st_mode);
if (!trust_executable_bit) {
/* If there is an existing entry, pick the mode bits
* from it.
*/
int pos = cache_name_pos(path, namelen);
if (pos >= 0)
ce->ce_mode = active_cache[pos]->ce_mode;
}

if (index_path(ce->sha1, path, &st, 1))
die("unable to index file %s", path);
if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD))
die("unable to add %s to index",path);
if (verbose)
printf("add '%s'\n", path);
return 0;
}

static struct cache_file cache_file;

int cmd_add(int argc, const char **argv, char **envp)
{
int i, newfd;
int verbose = 0, show_only = 0;
const char *prefix = setup_git_directory();
const char **pathspec;
struct dir_struct dir;

git_config(git_default_config);

newfd = hold_index_file_for_update(&cache_file, get_index_file());
if (newfd < 0)
die("unable to create new cachefile");

if (read_cache() < 0)
die("index file corrupt");

for (i = 1; i < argc; i++) {
const char *arg = argv[i];

if (arg[0] != '-')
break;
if (!strcmp(arg, "--")) {
i++;
break;
}
if (!strcmp(arg, "-n")) {
show_only = 1;
continue;
}
if (!strcmp(arg, "-v")) {
verbose = 1;
continue;
}
die(builtin_add_usage);
}
git_config(git_default_config);
pathspec = get_pathspec(prefix, argv + i);

fill_directory(&dir, pathspec);

if (show_only) {
const char *sep = "", *eof = "";
for (i = 0; i < dir.nr; i++) {
printf("%s%s", sep, dir.entries[i]->name);
sep = " ";
eof = "\n";
}
fputs(eof, stdout);
return 0;
}

for (i = 0; i < dir.nr; i++)
add_file_to_index(dir.entries[i]->name, verbose);

if (active_cache_changed) {
if (write_cache(newfd, active_cache, active_nr) ||
commit_index_file(&cache_file))
die("Unable to write new index file");
}

return 0;
}

3
apply.c → builtin-apply.c

@ -11,6 +11,7 @@ @@ -11,6 +11,7 @@
#include "quote.h"
#include "blob.h"
#include "delta.h"
#include "builtin.h"

// --check turns on checking that the working tree matches the
// files that are being modified, but doesn't apply the patch
@ -2168,7 +2169,7 @@ static int git_apply_config(const char *var, const char *value) @@ -2168,7 +2169,7 @@ static int git_apply_config(const char *var, const char *value)
}


int main(int argc, char **argv)
int cmd_apply(int argc, const char **argv, char **envp)
{
int i;
int read_stdin = 1;

3
cat-file.c → builtin-cat-file.c

@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
#include "exec_cmd.h"
#include "tag.h"
#include "tree.h"
#include "builtin.h"

static void flush_buffer(const char *buf, unsigned long size)
{
@ -93,7 +94,7 @@ static int pprint_tag(const unsigned char *sha1, const char *buf, unsigned long @@ -93,7 +94,7 @@ static int pprint_tag(const unsigned char *sha1, const char *buf, unsigned long
return 0;
}

int main(int argc, char **argv)
int cmd_cat_file(int argc, const char **argv, char **envp)
{
unsigned char sha1[20];
char type[20];

5
commit-tree.c → builtin-commit-tree.c

@ -6,6 +6,7 @@ @@ -6,6 +6,7 @@
#include "cache.h"
#include "commit.h"
#include "tree.h"
#include "builtin.h"

#define BLOCKING (1ul << 14)

@ -76,7 +77,7 @@ static int new_parent(int idx) @@ -76,7 +77,7 @@ static int new_parent(int idx)
return 1;
}

int main(int argc, char **argv)
int cmd_commit_tree(int argc, const char **argv, char **envp)
{
int i;
int parents = 0;
@ -98,7 +99,7 @@ int main(int argc, char **argv) @@ -98,7 +99,7 @@ int main(int argc, char **argv)

check_valid(tree_sha1, tree_type);
for (i = 2; i < argc; i += 2) {
char *a, *b;
const char *a, *b;
a = argv[i]; b = argv[i+1];
if (!b || strcmp(a, "-p"))
usage(commit_tree_usage);

3
diff-files.c → builtin-diff-files.c

@ -7,12 +7,13 @@ @@ -7,12 +7,13 @@
#include "diff.h"
#include "commit.h"
#include "revision.h"
#include "builtin.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;

int main(int argc, const char **argv)
int cmd_diff_files(int argc, const char **argv, char **envp)
{
struct rev_info rev;
int silent = 0;

3
diff-index.c → builtin-diff-index.c

@ -2,13 +2,14 @@ @@ -2,13 +2,14 @@
#include "diff.h"
#include "commit.h"
#include "revision.h"
#include "builtin.h"

static const char diff_cache_usage[] =
"git-diff-index [-m] [--cached] "
"[<common diff options>] <tree-ish> [<path>...]"
COMMON_DIFF_OPTIONS_HELP;

int main(int argc, const char **argv)
int cmd_diff_index(int argc, const char **argv, char **envp)
{
struct rev_info rev;
int cached = 0;

3
diff-stages.c → builtin-diff-stages.c

@ -4,6 +4,7 @@ @@ -4,6 +4,7 @@

#include "cache.h"
#include "diff.h"
#include "builtin.h"

static struct diff_options diff_options;

@ -54,7 +55,7 @@ static void diff_stages(int stage1, int stage2, const char **pathspec) @@ -54,7 +55,7 @@ static void diff_stages(int stage1, int stage2, const char **pathspec)
}
}

int main(int ac, const char **av)
int cmd_diff_stages(int ac, const char **av, char **envp)
{
int stage1, stage2;
const char *prefix = setup_git_directory();

3
diff-tree.c → builtin-diff-tree.c

@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
#include "diff.h"
#include "commit.h"
#include "log-tree.h"
#include "builtin.h"

static struct rev_info log_tree_opt;

@ -58,7 +59,7 @@ static const char diff_tree_usage[] = @@ -58,7 +59,7 @@ static const char diff_tree_usage[] =
" --root include the initial commit as diff against /dev/null\n"
COMMON_DIFF_OPTIONS_HELP;

int main(int argc, const char **argv)
int cmd_diff_tree(int argc, const char **argv, char **envp)
{
int nr_sha1;
char line[1000];

2
builtin-diff.c

@ -233,7 +233,7 @@ static int builtin_diff_combined(struct rev_info *revs, @@ -233,7 +233,7 @@ static int builtin_diff_combined(struct rev_info *revs,
return 0;
}

static void add_head(struct rev_info *revs)
void add_head(struct rev_info *revs)
{
unsigned char sha1[20];
struct object *obj;

172
builtin-log.c

@ -9,6 +9,10 @@ @@ -9,6 +9,10 @@
#include "diff.h"
#include "revision.h"
#include "log-tree.h"
#include "builtin.h"

/* this is in builtin-diff.c */
void add_head(struct rev_info *revs);

static int cmd_log_wc(int argc, const char **argv, char **envp,
struct rev_info *rev)
@ -74,3 +78,171 @@ int cmd_log(int argc, const char **argv, char **envp) @@ -74,3 +78,171 @@ int cmd_log(int argc, const char **argv, char **envp)
rev.diffopt.recursive = 1;
return cmd_log_wc(argc, argv, envp, &rev);
}

static int istitlechar(char c)
{
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') || c == '.' || c == '_';
}

static FILE *realstdout = NULL;
static char *output_directory = NULL;

static void reopen_stdout(struct commit *commit, int nr, int keep_subject)
{
char filename[1024];
char *sol;
int len = 0;

if (output_directory) {
strncpy(filename, output_directory, 1010);
len = strlen(filename);
if (filename[len - 1] != '/')
filename[len++] = '/';
}

sprintf(filename + len, "%04d", nr);
len = strlen(filename);

sol = strstr(commit->buffer, "\n\n");
if (sol) {
int j, space = 1;

sol += 2;
/* strip [PATCH] or [PATCH blabla] */
if (!keep_subject && !strncmp(sol, "[PATCH", 6)) {
char *eos = strchr(sol + 6, ']');
if (eos) {
while (isspace(*eos))
eos++;
sol = eos;
}
}

for (j = 0; len < 1024 - 6 && sol[j] && sol[j] != '\n'; j++) {
if (istitlechar(sol[j])) {
if (space) {
filename[len++] = '-';
space = 0;
}
filename[len++] = sol[j];
if (sol[j] == '.')
while (sol[j + 1] == '.')
j++;
} else
space = 1;
}
while (filename[len - 1] == '.' || filename[len - 1] == '-')
len--;
}
strcpy(filename + len, ".txt");
fprintf(realstdout, "%s\n", filename);
freopen(filename, "w", stdout);
}

int cmd_format_patch(int argc, const char **argv, char **envp)
{
struct commit *commit;
struct commit **list = NULL;
struct rev_info rev;
int nr = 0, total, i, j;
int use_stdout = 0;
int numbered = 0;
int keep_subject = 0;

init_revisions(&rev);
rev.commit_format = CMIT_FMT_EMAIL;
rev.verbose_header = 1;
rev.diff = 1;
rev.diffopt.with_raw = 0;
rev.diffopt.with_stat = 1;
rev.combine_merges = 0;
rev.ignore_merges = 1;
rev.diffopt.output_format = DIFF_FORMAT_PATCH;

/*
* Parse the arguments before setup_revisions(), or something
* like "git fmt-patch -o a123 HEAD^.." may fail; a123 is
* possibly a valid SHA1.
*/
for (i = 1, j = 1; i < argc; i++) {
if (!strcmp(argv[i], "--stdout"))
use_stdout = 1;
else if (!strcmp(argv[i], "-n") ||
!strcmp(argv[i], "--numbered"))
numbered = 1;
else if (!strcmp(argv[i], "-k") ||
!strcmp(argv[i], "--keep-subject")) {
keep_subject = 1;
rev.total = -1;
} else if (!strcmp(argv[i], "-o")) {
if (argc < 3)
die ("Which directory?");
if (mkdir(argv[i + 1], 0777) < 0 && errno != EEXIST)
die("Could not create directory %s",
argv[i + 1]);
output_directory = strdup(argv[i + 1]);
i++;
}
else if (!strcmp(argv[i], "--attach"))
rev.mime_boundary = git_version_string;
else if (!strncmp(argv[i], "--attach=", 9))
rev.mime_boundary = argv[i] + 9;
else
argv[j++] = argv[i];
}
argc = j;

if (numbered && keep_subject < 0)
die ("-n and -k are mutually exclusive.");

argc = setup_revisions(argc, argv, &rev, "HEAD");
if (argc > 1)
die ("unrecognized argument: %s", argv[1]);

if (rev.pending_objects && rev.pending_objects->next == NULL) {
rev.pending_objects->item->flags |= UNINTERESTING;
add_head(&rev);
}

if (!use_stdout)
realstdout = fdopen(dup(1), "w");

prepare_revision_walk(&rev);
while ((commit = get_revision(&rev)) != NULL) {
/* ignore merges */
if (commit->parents && commit->parents->next)
continue;
nr++;
list = realloc(list, nr * sizeof(list[0]));
list[nr - 1] = commit;
}
total = nr;
if (numbered)
rev.total = total;
while (0 <= --nr) {
int shown;
commit = list[nr];
rev.nr = total - nr;
if (!use_stdout)
reopen_stdout(commit, rev.nr, keep_subject);
shown = log_tree_commit(&rev, commit);
free(commit->buffer);
commit->buffer = NULL;
if (shown) {
if (rev.mime_boundary)
printf("\n--%s%s--\n\n\n",
mime_boundary_leader,
rev.mime_boundary);
else
printf("-- \n%s\n\n", git_version_string);
}
if (!use_stdout)
fclose(stdout);
}
if (output_directory)
free(output_directory);
free(list);
return 0;
}


384
ls-files.c → builtin-ls-files.c

@ -5,23 +5,21 @@ @@ -5,23 +5,21 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
#include <dirent.h>
#include <fnmatch.h>

#include "cache.h"
#include "quote.h"
#include "dir.h"
#include "builtin.h"

static int abbrev = 0;
static int show_deleted = 0;
static int show_cached = 0;
static int show_others = 0;
static int show_ignored = 0;
static int show_stage = 0;
static int show_unmerged = 0;
static int show_modified = 0;
static int show_killed = 0;
static int show_other_directories = 0;
static int hide_empty_directories = 0;
static int show_valid_bit = 0;
static int line_terminator = '\n';

@ -38,309 +36,6 @@ static const char *tag_other = ""; @@ -38,309 +36,6 @@ static const char *tag_other = "";
static const char *tag_killed = "";
static const char *tag_modified = "";

static const char *exclude_per_dir = NULL;

/* We maintain three exclude pattern lists:
* EXC_CMDL lists patterns explicitly given on the command line.
* EXC_DIRS lists patterns obtained from per-directory ignore files.
* EXC_FILE lists patterns from fallback ignore files.
*/
#define EXC_CMDL 0
#define EXC_DIRS 1
#define EXC_FILE 2
static struct exclude_list {
int nr;
int alloc;
struct exclude {
const char *pattern;
const char *base;
int baselen;
} **excludes;
} exclude_list[3];

static void add_exclude(const char *string, const char *base,
int baselen, struct exclude_list *which)
{
struct exclude *x = xmalloc(sizeof (*x));

x->pattern = string;
x->base = base;
x->baselen = baselen;
if (which->nr == which->alloc) {
which->alloc = alloc_nr(which->alloc);
which->excludes = realloc(which->excludes,
which->alloc * sizeof(x));
}
which->excludes[which->nr++] = x;
}

static int add_excludes_from_file_1(const char *fname,
const char *base,
int baselen,
struct exclude_list *which)
{
int fd, i;
long size;
char *buf, *entry;

fd = open(fname, O_RDONLY);
if (fd < 0)
goto err;
size = lseek(fd, 0, SEEK_END);
if (size < 0)
goto err;
lseek(fd, 0, SEEK_SET);
if (size == 0) {
close(fd);
return 0;
}
buf = xmalloc(size+1);
if (read(fd, buf, size) != size)
goto err;
close(fd);

buf[size++] = '\n';
entry = buf;
for (i = 0; i < size; i++) {
if (buf[i] == '\n') {
if (entry != buf + i && entry[0] != '#') {
buf[i - (i && buf[i-1] == '\r')] = 0;
add_exclude(entry, base, baselen, which);
}
entry = buf + i + 1;
}
}
return 0;

err:
if (0 <= fd)
close(fd);
return -1;
}

static void add_excludes_from_file(const char *fname)
{
if (add_excludes_from_file_1(fname, "", 0,
&exclude_list[EXC_FILE]) < 0)
die("cannot use %s as an exclude file", fname);
}

static int push_exclude_per_directory(const char *base, int baselen)
{
char exclude_file[PATH_MAX];
struct exclude_list *el = &exclude_list[EXC_DIRS];
int current_nr = el->nr;

if (exclude_per_dir) {
memcpy(exclude_file, base, baselen);
strcpy(exclude_file + baselen, exclude_per_dir);
add_excludes_from_file_1(exclude_file, base, baselen, el);
}
return current_nr;
}

static void pop_exclude_per_directory(int stk)
{
struct exclude_list *el = &exclude_list[EXC_DIRS];

while (stk < el->nr)
free(el->excludes[--el->nr]);
}

/* Scan the list and let the last match determines the fate.
* Return 1 for exclude, 0 for include and -1 for undecided.
*/
static int excluded_1(const char *pathname,
int pathlen,
struct exclude_list *el)
{
int i;

if (el->nr) {
for (i = el->nr - 1; 0 <= i; i--) {
struct exclude *x = el->excludes[i];
const char *exclude = x->pattern;
int to_exclude = 1;

if (*exclude == '!') {
to_exclude = 0;
exclude++;
}

if (!strchr(exclude, '/')) {
/* match basename */
const char *basename = strrchr(pathname, '/');
basename = (basename) ? basename+1 : pathname;
if (fnmatch(exclude, basename, 0) == 0)
return to_exclude;
}
else {
/* match with FNM_PATHNAME:
* exclude has base (baselen long) implicitly
* in front of it.
*/
int baselen = x->baselen;
if (*exclude == '/')
exclude++;

if (pathlen < baselen ||
(baselen && pathname[baselen-1] != '/') ||
strncmp(pathname, x->base, baselen))
continue;

if (fnmatch(exclude, pathname+baselen,
FNM_PATHNAME) == 0)
return to_exclude;
}
}
}
return -1; /* undecided */
}

static int excluded(const char *pathname)
{
int pathlen = strlen(pathname);
int st;

for (st = EXC_CMDL; st <= EXC_FILE; st++) {
switch (excluded_1(pathname, pathlen, &exclude_list[st])) {
case 0:
return 0;
case 1:
return 1;
}
}
return 0;
}

struct nond_on_fs {
int len;
char name[FLEX_ARRAY]; /* more */
};

static struct nond_on_fs **dir;
static int nr_dir;
static int dir_alloc;

static void add_name(const char *pathname, int len)
{
struct nond_on_fs *ent;

if (cache_name_pos(pathname, len) >= 0)
return;

if (nr_dir == dir_alloc) {
dir_alloc = alloc_nr(dir_alloc);
dir = xrealloc(dir, dir_alloc*sizeof(ent));
}
ent = xmalloc(sizeof(*ent) + len + 1);
ent->len = len;
memcpy(ent->name, pathname, len);
ent->name[len] = 0;
dir[nr_dir++] = ent;
}

static int dir_exists(const char *dirname, int len)
{
int pos = cache_name_pos(dirname, len);
if (pos >= 0)
return 1;
pos = -pos-1;
if (pos >= active_nr) /* can't */
return 0;
return !strncmp(active_cache[pos]->name, dirname, len);
}

/*
* Read a directory tree. We currently ignore anything but
* directories, regular files and symlinks. That's because git
* doesn't handle them at all yet. Maybe that will change some
* day.
*
* Also, we ignore the name ".git" (even if it is not a directory).
* That likely will not change.
*/
static int read_directory(const char *path, const char *base, int baselen)
{
DIR *fdir = opendir(path);
int contents = 0;

if (fdir) {
int exclude_stk;
struct dirent *de;
char fullname[MAXPATHLEN + 1];
memcpy(fullname, base, baselen);

exclude_stk = push_exclude_per_directory(base, baselen);

while ((de = readdir(fdir)) != NULL) {
int len;

if ((de->d_name[0] == '.') &&
(de->d_name[1] == 0 ||
!strcmp(de->d_name + 1, ".") ||
!strcmp(de->d_name + 1, "git")))
continue;
len = strlen(de->d_name);
memcpy(fullname + baselen, de->d_name, len+1);
if (excluded(fullname) != show_ignored) {
if (!show_ignored || DTYPE(de) != DT_DIR) {
continue;
}
}

switch (DTYPE(de)) {
struct stat st;
int subdir, rewind_base;
default:
continue;
case DT_UNKNOWN:
if (lstat(fullname, &st))
continue;
if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
break;
if (!S_ISDIR(st.st_mode))
continue;
/* fallthrough */
case DT_DIR:
memcpy(fullname + baselen + len, "/", 2);
len++;
rewind_base = nr_dir;
subdir = read_directory(fullname, fullname,
baselen + len);
if (show_other_directories &&
(subdir || !hide_empty_directories) &&
!dir_exists(fullname, baselen + len)) {
// Rewind the read subdirectory
while (nr_dir > rewind_base)
free(dir[--nr_dir]);
break;
}
contents += subdir;
continue;
case DT_REG:
case DT_LNK:
break;
}
add_name(fullname, baselen + len);
contents++;
}
closedir(fdir);

pop_exclude_per_directory(exclude_stk);
}

return contents;
}

static int cmp_name(const void *p1, const void *p2)
{
const struct nond_on_fs *e1 = *(const struct nond_on_fs **)p1;
const struct nond_on_fs *e2 = *(const struct nond_on_fs **)p2;

return cache_name_compare(e1->name, e1->len,
e2->name, e2->len);
}

/*
* Match a pathspec against a filename. The first "len" characters
@ -377,7 +72,7 @@ static int match(const char **spec, char *ps_matched, @@ -377,7 +72,7 @@ static int match(const char **spec, char *ps_matched,
return 0;
}

static void show_dir_entry(const char *tag, struct nond_on_fs *ent)
static void show_dir_entry(const char *tag, struct dir_entry *ent)
{
int len = prefix_len;
int offset = prefix_offset;
@ -393,14 +88,14 @@ static void show_dir_entry(const char *tag, struct nond_on_fs *ent) @@ -393,14 +88,14 @@ static void show_dir_entry(const char *tag, struct nond_on_fs *ent)
putchar(line_terminator);
}

static void show_other_files(void)
static void show_other_files(struct dir_struct *dir)
{
int i;
for (i = 0; i < nr_dir; i++) {
for (i = 0; i < dir->nr; i++) {
/* We should not have a matching entry, but we
* may have an unmerged entry for this path.
*/
struct nond_on_fs *ent = dir[i];
struct dir_entry *ent = dir->entries[i];
int pos = cache_name_pos(ent->name, ent->len);
struct cache_entry *ce;
if (0 <= pos)
@ -416,11 +111,11 @@ static void show_other_files(void) @@ -416,11 +111,11 @@ static void show_other_files(void)
}
}

static void show_killed_files(void)
static void show_killed_files(struct dir_struct *dir)
{
int i;
for (i = 0; i < nr_dir; i++) {
struct nond_on_fs *ent = dir[i];
for (i = 0; i < dir->nr; i++) {
struct dir_entry *ent = dir->entries[i];
char *cp, *sp;
int pos, len, killed = 0;

@ -461,7 +156,7 @@ static void show_killed_files(void) @@ -461,7 +156,7 @@ static void show_killed_files(void)
}
}
if (killed)
show_dir_entry(tag_killed, dir[i]);
show_dir_entry(tag_killed, dir->entries[i]);
}
}

@ -512,7 +207,7 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce) @@ -512,7 +207,7 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
}
}

static void show_files(void)
static void show_files(struct dir_struct *dir)
{
int i;

@ -521,39 +216,18 @@ static void show_files(void) @@ -521,39 +216,18 @@ static void show_files(void)
const char *path = ".", *base = "";
int baselen = prefix_len;

if (baselen) {
if (baselen)
path = base = prefix;
if (exclude_per_dir) {
char *p, *pp = xmalloc(baselen+1);
memcpy(pp, prefix, baselen+1);
p = pp;
while (1) {
char save = *p;
*p = 0;
push_exclude_per_directory(pp, p-pp);
*p++ = save;
if (!save)
break;
p = strchr(p, '/');
if (p)
p++;
else
p = pp + baselen;
}
free(pp);
}
}
read_directory(path, base, baselen);
qsort(dir, nr_dir, sizeof(struct nond_on_fs *), cmp_name);
read_directory(dir, path, base, baselen);
if (show_others)
show_other_files();
show_other_files(dir);
if (show_killed)
show_killed_files();
show_killed_files(dir);
}
if (show_cached | show_stage) {
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = active_cache[i];
if (excluded(ce->name) != show_ignored)
if (excluded(dir, ce->name) != dir->show_ignored)
continue;
if (show_unmerged && !ce_stage(ce))
continue;
@ -565,7 +239,7 @@ static void show_files(void) @@ -565,7 +239,7 @@ static void show_files(void)
struct cache_entry *ce = active_cache[i];
struct stat st;
int err;
if (excluded(ce->name) != show_ignored)
if (excluded(dir, ce->name) != dir->show_ignored)
continue;
err = lstat(ce->name, &st);
if (show_deleted && err)
@ -648,11 +322,13 @@ static const char ls_files_usage[] = @@ -648,11 +322,13 @@ static const char ls_files_usage[] =
"[ --exclude-per-directory=<filename> ] [--full-name] [--abbrev] "
"[--] [<file>]*";

int main(int argc, const char **argv)
int cmd_ls_files(int argc, const char **argv, char** envp)
{
int i;
int exc_given = 0;
struct dir_struct dir;

memset(&dir, 0, sizeof(dir));
prefix = setup_git_directory();
if (prefix)
prefix_offset = strlen(prefix);
@ -697,7 +373,7 @@ int main(int argc, const char **argv) @@ -697,7 +373,7 @@ int main(int argc, const char **argv)
continue;
}
if (!strcmp(arg, "-i") || !strcmp(arg, "--ignored")) {
show_ignored = 1;
dir.show_ignored = 1;
continue;
}
if (!strcmp(arg, "-s") || !strcmp(arg, "--stage")) {
@ -709,11 +385,11 @@ int main(int argc, const char **argv) @@ -709,11 +385,11 @@ int main(int argc, const char **argv)
continue;
}
if (!strcmp(arg, "--directory")) {
show_other_directories = 1;
dir.show_other_directories = 1;
continue;
}
if (!strcmp(arg, "--no-empty-directory")) {
hide_empty_directories = 1;
dir.hide_empty_directories = 1;
continue;
}
if (!strcmp(arg, "-u") || !strcmp(arg, "--unmerged")) {
@ -726,27 +402,27 @@ int main(int argc, const char **argv) @@ -726,27 +402,27 @@ int main(int argc, const char **argv)
}
if (!strcmp(arg, "-x") && i+1 < argc) {
exc_given = 1;
add_exclude(argv[++i], "", 0, &exclude_list[EXC_CMDL]);
add_exclude(argv[++i], "", 0, &dir.exclude_list[EXC_CMDL]);
continue;
}
if (!strncmp(arg, "--exclude=", 10)) {
exc_given = 1;
add_exclude(arg+10, "", 0, &exclude_list[EXC_CMDL]);
add_exclude(arg+10, "", 0, &dir.exclude_list[EXC_CMDL]);
continue;
}
if (!strcmp(arg, "-X") && i+1 < argc) {
exc_given = 1;
add_excludes_from_file(argv[++i]);
add_excludes_from_file(&dir, argv[++i]);
continue;
}
if (!strncmp(arg, "--exclude-from=", 15)) {
exc_given = 1;
add_excludes_from_file(arg+15);
add_excludes_from_file(&dir, arg+15);
continue;
}
if (!strncmp(arg, "--exclude-per-directory=", 24)) {
exc_given = 1;
exclude_per_dir = arg + 24;
dir.exclude_per_dir = arg + 24;
continue;
}
if (!strcmp(arg, "--full-name")) {
@ -788,7 +464,7 @@ int main(int argc, const char **argv) @@ -788,7 +464,7 @@ int main(int argc, const char **argv)
ps_matched = xcalloc(1, num);
}

if (show_ignored && !exc_given) {
if (dir.show_ignored && !exc_given) {
fprintf(stderr, "%s: --ignored needs some exclude pattern\n",
argv[0]);
exit(1);
@ -802,7 +478,7 @@ int main(int argc, const char **argv) @@ -802,7 +478,7 @@ int main(int argc, const char **argv)
read_cache();
if (prefix)
prune_cache();
show_files();
show_files(&dir);

if (ps_matched) {
/* We need to make sure all pathspec matched otherwise

5
ls-tree.c → builtin-ls-tree.c

@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
#include "blob.h"
#include "tree.h"
#include "quote.h"
#include "builtin.h"

static int line_termination = '\n';
#define LS_RECURSIVE 1
@ -15,7 +16,7 @@ static int line_termination = '\n'; @@ -15,7 +16,7 @@ static int line_termination = '\n';
#define LS_NAME_ONLY 8
static int abbrev = 0;
static int ls_options = 0;
const char **pathspec;
static const char **pathspec;
static int chomp_prefix = 0;
static const char *prefix;

@ -84,7 +85,7 @@ static int show_tree(unsigned char *sha1, const char *base, int baselen, @@ -84,7 +85,7 @@ static int show_tree(unsigned char *sha1, const char *base, int baselen,
return retval;
}

int main(int argc, const char **argv)
int cmd_ls_tree(int argc, const char **argv, char **envp)
{
unsigned char sha1[20];
struct tree *tree;

3
read-tree.c → builtin-read-tree.c

@ -11,6 +11,7 @@ @@ -11,6 +11,7 @@
#include "tree.h"
#include <sys/time.h>
#include <signal.h>
#include "builtin.h"

static int reset = 0;
static int merge = 0;
@ -763,7 +764,7 @@ static const char read_tree_usage[] = "git-read-tree (<sha> | -m [--aggressive] @@ -763,7 +764,7 @@ static const char read_tree_usage[] = "git-read-tree (<sha> | -m [--aggressive]

static struct cache_file cache_file;

int main(int argc, char **argv)
int cmd_read_tree(int argc, const char **argv, char **envp)
{
int i, newfd, stage = 0;
unsigned char sha1[20];

2
builtin-rev-list.c

@ -85,7 +85,7 @@ static void show_commit(struct commit *commit) @@ -85,7 +85,7 @@ static void show_commit(struct commit *commit)
static char pretty_header[16384];
pretty_print_commit(revs.commit_format, commit, ~0,
pretty_header, sizeof(pretty_header),
revs.abbrev);
revs.abbrev, NULL, NULL);
printf("%s%c", pretty_header, hdr_termination);
}
fflush(stdout);

150
builtin-rm.c

@ -0,0 +1,150 @@ @@ -0,0 +1,150 @@
/*
* "git rm" builtin command
*
* Copyright (C) Linus Torvalds 2006
*/
#include "cache.h"
#include "builtin.h"
#include "dir.h"

static const char builtin_rm_usage[] =
"git-rm [-n] [-v] [-f] <filepattern>...";

static struct {
int nr, alloc;
const char **name;
} list;

static void add_list(const char *name)
{
if (list.nr >= list.alloc) {
list.alloc = alloc_nr(list.alloc);
list.name = xrealloc(list.name, list.alloc * sizeof(const char *));
}
list.name[list.nr++] = name;
}

static int remove_file(const char *name)
{
int ret;
char *slash;

ret = unlink(name);
if (!ret && (slash = strrchr(name, '/'))) {
char *n = strdup(name);
do {
n[slash - name] = 0;
name = n;
} while (!rmdir(name) && (slash = strrchr(name, '/')));
}
return ret;
}

static struct cache_file cache_file;

int cmd_rm(int argc, const char **argv, char **envp)
{
int i, newfd;
int verbose = 0, show_only = 0, force = 0;
const char *prefix = setup_git_directory();
const char **pathspec;
char *seen;

git_config(git_default_config);

newfd = hold_index_file_for_update(&cache_file, get_index_file());
if (newfd < 0)
die("unable to create new index file");

if (read_cache() < 0)
die("index file corrupt");

for (i = 1 ; i < argc ; i++) {
const char *arg = argv[i];

if (*arg != '-')
break;
if (!strcmp(arg, "--")) {
i++;
break;
}
if (!strcmp(arg, "-n")) {
show_only = 1;
continue;
}
if (!strcmp(arg, "-v")) {
verbose = 1;
continue;
}
if (!strcmp(arg, "-f")) {
force = 1;
continue;
}
die(builtin_rm_usage);
}
pathspec = get_pathspec(prefix, argv + i);

seen = NULL;
if (pathspec) {
for (i = 0; pathspec[i] ; i++)
/* nothing */;
seen = xmalloc(i);
memset(seen, 0, i);
}

for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = active_cache[i];
if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen))
continue;
add_list(ce->name);
}

if (pathspec) {
const char *match;
for (i = 0; (match = pathspec[i]) != NULL ; i++) {
if (*match && !seen[i])
die("pathspec '%s' did not match any files", match);
}
}

/*
* First remove the names from the index: we won't commit
* the index unless all of them succeed
*/
for (i = 0; i < list.nr; i++) {
const char *path = list.name[i];
printf("rm '%s'\n", path);

if (remove_file_from_cache(path))
die("git rm: unable to remove %s", path);
}

/*
* Then, if we used "-f", remove the filenames from the
* workspace. If we fail to remove the first one, we
* abort the "git rm" (but once we've successfully removed
* any file at all, we'll go ahead and commit to it all:
* by then we've already committed ourself and can't fail
* in the middle)
*/
if (force) {
int removed = 0;
for (i = 0; i < list.nr; i++) {
const char *path = list.name[i];
if (!remove_file(path)) {
removed = 1;
continue;
}
if (!removed)
die("git rm: %s: %s", path, strerror(errno));
}
}

if (active_cache_changed) {
if (write_cache(newfd, active_cache, active_nr) ||
commit_index_file(&cache_file))
die("Unable to write new index file");
}

return 0;
}

9
show-branch.c → builtin-show-branch.c

@ -3,13 +3,14 @@ @@ -3,13 +3,14 @@
#include "cache.h"
#include "commit.h"
#include "refs.h"
#include "builtin.h"

static const char show_branch_usage[] =
"git-show-branch [--dense] [--current] [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [<refs>...]";

static int default_num = 0;
static int default_alloc = 0;
static char **default_arg = NULL;
static const char **default_arg = NULL;

#define UNINTERESTING 01

@ -259,7 +260,7 @@ static void show_one_commit(struct commit *commit, int no_name) @@ -259,7 +260,7 @@ static void show_one_commit(struct commit *commit, int no_name)
struct commit_name *name = commit->object.util;
if (commit->object.parsed)
pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
pretty, sizeof(pretty), 0);
pretty, sizeof(pretty), 0, NULL, NULL);
else
strcpy(pretty, "(unavailable)");
if (!strncmp(pretty, "[PATCH] ", 8))
@ -548,7 +549,7 @@ static int omit_in_dense(struct commit *commit, struct commit **rev, int n) @@ -548,7 +549,7 @@ static int omit_in_dense(struct commit *commit, struct commit **rev, int n)
return 0;
}

int main(int ac, char **av)
int cmd_show_branch(int ac, const char **av, char **envp)
{
struct commit *rev[MAX_REVS], *commit;
struct commit_list *list = NULL, *seen = NULL;
@ -581,7 +582,7 @@ int main(int ac, char **av) @@ -581,7 +582,7 @@ int main(int ac, char **av)
}

while (1 < ac && av[1][0] == '-') {
char *arg = av[1];
const char *arg = av[1];
if (!strcmp(arg, "--")) {
ac--; av++;
break;

62
tar-tree.c → builtin-tar-tree.c

@ -7,11 +7,14 @@ @@ -7,11 +7,14 @@
#include "commit.h"
#include "strbuf.h"
#include "tar.h"
#include "builtin.h"
#include "pkt-line.h"

#define RECORDSIZE (512)
#define BLOCKSIZE (RECORDSIZE * 20)

static const char tar_tree_usage[] = "git-tar-tree <key> [basedir]";
static const char tar_tree_usage[] =
"git-tar-tree [--remote=<repo>] <ent> [basedir]";

static char block[BLOCKSIZE];
static unsigned long offset;
@ -301,7 +304,7 @@ static void traverse_tree(struct tree_desc *tree, struct strbuf *path) @@ -301,7 +304,7 @@ static void traverse_tree(struct tree_desc *tree, struct strbuf *path)
}
}

int main(int argc, char **argv)
static int generate_tar(int argc, const char **argv, char** envp)
{
unsigned char sha1[20], tree_sha1[20];
struct commit *commit;
@ -348,3 +351,58 @@ int main(int argc, char **argv) @@ -348,3 +351,58 @@ int main(int argc, char **argv)
free(current_path.buf);
return 0;
}

static const char *exec = "git-upload-tar";

static int remote_tar(int argc, const char **argv)
{
int fd[2], ret, len;
pid_t pid;
char buf[1024];
char *url;

if (argc < 3 || 4 < argc)
usage(tar_tree_usage);

/* --remote=<repo> */
url = strdup(argv[1]+9);
pid = git_connect(fd, url, exec);
if (pid < 0)
return 1;

packet_write(fd[1], "want %s\n", argv[2]);
if (argv[3])
packet_write(fd[1], "base %s\n", argv[3]);
packet_flush(fd[1]);

len = packet_read_line(fd[0], buf, sizeof(buf));
if (!len)
die("git-tar-tree: expected ACK/NAK, got EOF");
if (buf[len-1] == '\n')
buf[--len] = 0;
if (strcmp(buf, "ACK")) {
if (5 < len && !strncmp(buf, "NACK ", 5))
die("git-tar-tree: NACK %s", buf + 5);
die("git-tar-tree: protocol error");
}
/* expect a flush */
len = packet_read_line(fd[0], buf, sizeof(buf));
if (len)
die("git-tar-tree: expected a flush");

/* Now, start reading from fd[0] and spit it out to stdout */
ret = copy_fd(fd[0], 1);
close(fd[0]);

ret |= finish_connect(pid);
return !!ret;
}

int cmd_tar_tree(int argc, const char **argv, char **envp)
{
if (argc < 2)
usage(tar_tree_usage);
if (!strncmp("--remote=", argv[1], 9))
return remote_tar(argc, argv);
return generate_tar(argc, argv, envp);
}

74
builtin-upload-tar.c

@ -0,0 +1,74 @@ @@ -0,0 +1,74 @@
/*
* Copyright (c) 2006 Junio C Hamano
*/
#include "cache.h"
#include "pkt-line.h"
#include "exec_cmd.h"
#include "builtin.h"

static const char upload_tar_usage[] = "git-upload-tar <repo>";

static int nak(const char *reason)
{
packet_write(1, "NACK %s\n", reason);
packet_flush(1);
return 1;
}

int cmd_upload_tar(int argc, const char **argv, char **envp)
{
int len;
const char *dir = argv[1];
char buf[8192];
unsigned char sha1[20];
char *base = NULL;
char hex[41];
int ac;
const char *av[4];

if (argc != 2)
usage(upload_tar_usage);
if (strlen(dir) < sizeof(buf)-1)
strcpy(buf, dir); /* enter-repo smudges its argument */
else
packet_write(1, "NACK insanely long repository name %s\n", dir);
if (!enter_repo(buf, 0)) {
packet_write(1, "NACK not a git archive %s\n", dir);
packet_flush(1);
return 1;
}

len = packet_read_line(0, buf, sizeof(buf));
if (len < 5 || strncmp("want ", buf, 5))
return nak("expected want");
if (buf[len-1] == '\n')
buf[--len] = 0;
if (get_sha1(buf + 5, sha1))
return nak("expected sha1");
strcpy(hex, sha1_to_hex(sha1));

len = packet_read_line(0, buf, sizeof(buf));
if (len) {
if (len < 5 || strncmp("base ", buf, 5))
return nak("expected (optional) base");
if (buf[len-1] == '\n')
buf[--len] = 0;
base = strdup(buf + 5);
len = packet_read_line(0, buf, sizeof(buf));
}
if (len)
return nak("expected flush");

packet_write(1, "ACK\n");
packet_flush(1);

ac = 0;
av[ac++] = "tar-tree";
av[ac++] = hex;
if (base)
av[ac++] = base;
av[ac++] = NULL;
execv_git_cmd(av);
/* should it return that is an error */
return 1;
}

16
builtin.h

@ -20,12 +20,28 @@ extern int cmd_whatchanged(int argc, const char **argv, char **envp); @@ -20,12 +20,28 @@ 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);
extern int cmd_diff(int argc, const char **argv, char **envp);
extern int cmd_format_patch(int argc, const char **argv, char **envp);
extern int cmd_count_objects(int argc, const char **argv, char **envp);

extern int cmd_push(int argc, const char **argv, char **envp);
extern int cmd_grep(int argc, const char **argv, char **envp);
extern int cmd_rm(int argc, const char **argv, char **envp);
extern int cmd_add(int argc, const char **argv, char **envp);
extern int cmd_rev_list(int argc, const char **argv, char **envp);
extern int cmd_check_ref_format(int argc, const char **argv, char **envp);
extern int cmd_init_db(int argc, const char **argv, char **envp);
extern int cmd_tar_tree(int argc, const char **argv, char **envp);
extern int cmd_upload_tar(int argc, const char **argv, char **envp);
extern int cmd_ls_files(int argc, const char **argv, char **envp);
extern int cmd_ls_tree(int argc, const char **argv, char **envp);
extern int cmd_read_tree(int argc, const char **argv, char **envp);
extern int cmd_commit_tree(int argc, const char **argv, char **envp);
extern int cmd_apply(int argc, const char **argv, char **envp);
extern int cmd_show_branch(int argc, const char **argv, char **envp);
extern int cmd_diff_files(int argc, const char **argv, char **envp);
extern int cmd_diff_index(int argc, const char **argv, char **envp);
extern int cmd_diff_stages(int argc, const char **argv, char **envp);
extern int cmd_diff_tree(int argc, const char **argv, char **envp);
extern int cmd_cat_file(int argc, const char **argv, char **envp);

#endif

3
cache.h

@ -142,6 +142,7 @@ extern void verify_non_filename(const char *prefix, const char *name); @@ -142,6 +142,7 @@ extern void verify_non_filename(const char *prefix, const char *name);
/* Initialize and use the cache information */
extern int read_cache(void);
extern int write_cache(int newfd, struct cache_entry **cache, int entries);
extern int verify_path(const char *path);
extern int cache_name_pos(const char *name, int namelen);
#define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */
#define ADD_CACHE_OK_TO_REPLACE 2 /* Ok to replace file/directory */
@ -154,6 +155,7 @@ extern int ce_match_stat(struct cache_entry *ce, struct stat *st, int); @@ -154,6 +155,7 @@ extern int ce_match_stat(struct cache_entry *ce, struct stat *st, int);
extern int ce_modified(struct cache_entry *ce, struct stat *st, int);
extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, const char *type);
extern int read_pipe(int fd, char** return_buf, unsigned long* return_size);
extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object);
extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
@ -257,6 +259,7 @@ extern void *read_object_with_reference(const unsigned char *sha1, @@ -257,6 +259,7 @@ extern void *read_object_with_reference(const unsigned char *sha1,
unsigned char *sha1_ret);

const char *show_date(unsigned long time, int timezone);
const char *show_rfc2822_date(unsigned long time, int timezone);
int parse_date(const char *date, char *buf, int bufsize);
void datestamp(char *buf, int bufsize);
unsigned long approxidate(const char *);

134
commit.c

@ -30,6 +30,7 @@ struct cmt_fmt_map { @@ -30,6 +30,7 @@ struct cmt_fmt_map {
{ "raw", 1, CMIT_FMT_RAW },
{ "medium", 1, CMIT_FMT_MEDIUM },
{ "short", 1, CMIT_FMT_SHORT },
{ "email", 1, CMIT_FMT_EMAIL },
{ "full", 5, CMIT_FMT_FULL },
{ "fuller", 5, CMIT_FMT_FULLER },
{ "oneline", 1, CMIT_FMT_ONELINE },
@ -421,6 +422,46 @@ static int get_one_line(const char *msg, unsigned long len) @@ -421,6 +422,46 @@ static int get_one_line(const char *msg, unsigned long len)
return ret;
}

static int is_rfc2047_special(char ch)
{
return ((ch & 0x80) || (ch == '=') || (ch == '?') || (ch == '_'));
}

static int add_rfc2047(char *buf, const char *line, int len)
{
char *bp = buf;
int i, needquote;
static const char q_utf8[] = "=?utf-8?q?";

for (i = needquote = 0; !needquote && i < len; i++) {
unsigned ch = line[i];
if (ch & 0x80)
needquote++;
if ((i + 1 < len) &&
(ch == '=' && line[i+1] == '?'))
needquote++;
}
if (!needquote)
return sprintf(buf, "%.*s", len, line);

memcpy(bp, q_utf8, sizeof(q_utf8)-1);
bp += sizeof(q_utf8)-1;
for (i = 0; i < len; i++) {
unsigned ch = line[i];
if (is_rfc2047_special(ch)) {
sprintf(bp, "=%02X", ch);
bp += 3;
}
else if (ch == ' ')
*bp++ = '_';
else
*bp++ = ch;
}
memcpy(bp, "?=", 2);
bp += 2;
return bp - buf;
}

static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const char *line)
{
char *date;
@ -438,13 +479,35 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c @@ -438,13 +479,35 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c
time = strtoul(date, &date, 10);
tz = strtol(date, NULL, 10);

ret = sprintf(buf, "%s: %.*s%.*s\n", what,
(fmt == CMIT_FMT_FULLER) ? 4 : 0,
filler, namelen, line);
if (fmt == CMIT_FMT_EMAIL) {
char *name_tail = strchr(line, '<');
int display_name_length;
if (!name_tail)
return 0;
while (line < name_tail && isspace(name_tail[-1]))
name_tail--;
display_name_length = name_tail - line;
filler = "";
strcpy(buf, "From: ");
ret = strlen(buf);
ret += add_rfc2047(buf + ret, line, display_name_length);
memcpy(buf + ret, name_tail, namelen - display_name_length);
ret += namelen - display_name_length;
buf[ret++] = '\n';
}
else {
ret = sprintf(buf, "%s: %.*s%.*s\n", what,
(fmt == CMIT_FMT_FULLER) ? 4 : 0,
filler, namelen, line);
}
switch (fmt) {
case CMIT_FMT_MEDIUM:
ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz));
break;
case CMIT_FMT_EMAIL:
ret += sprintf(buf + ret, "Date: %s\n",
show_rfc2822_date(time, tz));
break;
case CMIT_FMT_FULLER:
ret += sprintf(buf + ret, "%sDate: %s\n", what, show_date(time, tz));
break;
@ -455,10 +518,12 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c @@ -455,10 +518,12 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c
return ret;
}

static int is_empty_line(const char *line, int len)
static int is_empty_line(const char *line, int *len_p)
{
int len = *len_p;
while (len && isspace(line[len-1]))
len--;
*len_p = len;
return !len;
}

@ -467,7 +532,8 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com @@ -467,7 +532,8 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com
struct commit_list *parent = commit->parents;
int offset;

if ((fmt == CMIT_FMT_ONELINE) || !parent || !parent->next)
if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) ||
!parent || !parent->next)
return 0;

offset = sprintf(buf, "Merge:");
@ -486,13 +552,30 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com @@ -486,13 +552,30 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com
return offset;
}

unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, char *buf, unsigned long space, int abbrev)
unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject, const char *after_subject)
{
int hdr = 1, body = 0;
unsigned long offset = 0;
int indent = (fmt == CMIT_FMT_ONELINE) ? 0 : 4;
int indent = 4;
int parents_shown = 0;
const char *msg = commit->buffer;
int plain_non_ascii = 0;

if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
indent = 0;

/* After-subject is used to pass in Content-Type: multipart
* MIME header; in that case we do not have to do the
* plaintext content type even if the commit message has
* non 7-bit ASCII character. Otherwise, check if we need
* to say this is not a 7-bit ASCII.
*/
if (fmt == CMIT_FMT_EMAIL && !after_subject) {
int i;
for (i = 0; !plain_non_ascii && msg[i] && i < len; i++)
if (msg[i] & 0x80)
plain_non_ascii = 1;
}

for (;;) {
const char *line = msg;
@ -516,7 +599,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit @@ -516,7 +599,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit
if (hdr) {
if (linelen == 1) {
hdr = 0;
if (fmt != CMIT_FMT_ONELINE)
if ((fmt != CMIT_FMT_ONELINE) && !subject)
buf[offset++] = '\n';
continue;
}
@ -554,20 +637,47 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit @@ -554,20 +637,47 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit
continue;
}

if (is_empty_line(line, linelen)) {
if (is_empty_line(line, &linelen)) {
if (!body)
continue;
if (subject)
continue;
if (fmt == CMIT_FMT_SHORT)
break;
} else {
body = 1;
}

memset(buf + offset, ' ', indent);
memcpy(buf + offset + indent, line, linelen);
offset += linelen + indent;
if (subject) {
int slen = strlen(subject);
memcpy(buf + offset, subject, slen);
offset += slen;
offset += add_rfc2047(buf + offset, line, linelen);
}
else {
memset(buf + offset, ' ', indent);
memcpy(buf + offset + indent, line, linelen);
offset += linelen + indent;
}
buf[offset++] = '\n';
if (fmt == CMIT_FMT_ONELINE)
break;
if (subject && plain_non_ascii) {
static const char header[] =
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n";
memcpy(buf + offset, header, sizeof(header)-1);
offset += sizeof(header)-1;
}
if (after_subject) {
int slen = strlen(after_subject);
if (slen > space - offset - 1)
slen = space - offset - 1;
memcpy(buf + offset, after_subject, slen);
offset += slen;
after_subject = NULL;
}
subject = NULL;
}
while (offset && isspace(buf[offset-1]))
offset--;

3
commit.h

@ -45,12 +45,13 @@ enum cmit_fmt { @@ -45,12 +45,13 @@ enum cmit_fmt {
CMIT_FMT_FULL,
CMIT_FMT_FULLER,
CMIT_FMT_ONELINE,
CMIT_FMT_EMAIL,

CMIT_FMT_UNSPECIFIED,
};

extern enum cmit_fmt get_commit_format(const char *arg);
extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char *buf, unsigned long space, int abbrev);
extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject, const char *after_subject);

/** Removes the first commit from a list sorted by date, and adds all
* of its parents.

2
connect.c

@ -100,7 +100,7 @@ int path_match(const char *path, int nr, char **match) @@ -100,7 +100,7 @@ int path_match(const char *path, int nr, char **match)
if (pathlen > len && path[pathlen - len - 1] != '/')
continue;
*s = 0;
return 1;
return (i + 1);
}
return 0;
}

29
date.c

@ -42,18 +42,24 @@ static const char *weekday_names[] = { @@ -42,18 +42,24 @@ static const char *weekday_names[] = {
* thing, which means that tz -0100 is passed in as the integer -100,
* even though it means "sixty minutes off"
*/
const char *show_date(unsigned long time, int tz)
static struct tm *time_to_tm(unsigned long time, int tz)
{
struct tm *tm;
time_t t;
static char timebuf[200];
int minutes;

minutes = tz < 0 ? -tz : tz;
minutes = (minutes / 100)*60 + (minutes % 100);
minutes = tz < 0 ? -minutes : minutes;
t = time + minutes * 60;
tm = gmtime(&t);
return gmtime(&t);
}

const char *show_date(unsigned long time, int tz)
{
struct tm *tm;
static char timebuf[200];

tm = time_to_tm(time, tz);
if (!tm)
return NULL;
sprintf(timebuf, "%.3s %.3s %d %02d:%02d:%02d %d %+05d",
@ -65,6 +71,21 @@ const char *show_date(unsigned long time, int tz) @@ -65,6 +71,21 @@ const char *show_date(unsigned long time, int tz)
return timebuf;
}

const char *show_rfc2822_date(unsigned long time, int tz)
{
struct tm *tm;
static char timebuf[200];

tm = time_to_tm(time, tz);
if (!tm)
return NULL;
sprintf(timebuf, "%.3s, %d %.3s %d %02d:%02d:%02d %+05d",
weekday_names[tm->tm_wday], tm->tm_mday,
month_names[tm->tm_mon], tm->tm_year + 1900,
tm->tm_hour, tm->tm_min, tm->tm_sec, tz);
return timebuf;
}

/*
* Check these. And note how it doesn't do the summer-time conversion.
*

11
diff.c

@ -299,6 +299,7 @@ static void diffstat_consume(void *priv, char *line, unsigned long len) @@ -299,6 +299,7 @@ static void diffstat_consume(void *priv, char *line, unsigned long len)

static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
static const char minuses[]= "----------------------------------------------------------------------";
const char mime_boundary_leader[] = "------------";

static void show_stats(struct diffstat_t* data)
{
@ -1988,7 +1989,13 @@ void diff_flush(struct diff_options *options) @@ -1988,7 +1989,13 @@ void diff_flush(struct diff_options *options)
show_stats(diffstat);
free(diffstat);
diffstat = NULL;
putchar(options->line_termination);
if (options->summary)
for (i = 0; i < q->nr; i++)
diff_summary(q->queue[i]);
if (options->stat_sep)
fputs(options->stat_sep, stdout);
else
putchar(options->line_termination);
}
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
@ -2001,7 +2008,7 @@ void diff_flush(struct diff_options *options) @@ -2001,7 +2008,7 @@ void diff_flush(struct diff_options *options)
}

for (i = 0; i < q->nr; i++) {
if (options->summary)
if (diffstat && options->summary)
diff_summary(q->queue[i]);
diff_free_filepair(q->queue[i]);
}

3
diff.h

@ -44,6 +44,7 @@ struct diff_options { @@ -44,6 +44,7 @@ struct diff_options {
int rename_limit;
int setup;
int abbrev;
const char *stat_sep;

int nr_paths;
const char **paths;
@ -52,6 +53,8 @@ struct diff_options { @@ -52,6 +53,8 @@ struct diff_options {
add_remove_fn_t add_remove;
};

extern const char mime_boundary_leader[];

extern void diff_tree_setup_paths(const char **paths, struct diff_options *);
extern void diff_tree_release_paths(struct diff_options *);
extern int diff_tree(struct tree_desc *t1, struct tree_desc *t2,

401
dir.c

@ -0,0 +1,401 @@ @@ -0,0 +1,401 @@
/*
* This handles recursive filename detection with exclude
* files, index knowledge etc..
*
* Copyright (C) Linus Torvalds, 2005-2006
* Junio Hamano, 2005-2006
*/
#include <dirent.h>
#include <fnmatch.h>

#include "cache.h"
#include "dir.h"

int common_prefix(const char **pathspec)
{
const char *path, *slash, *next;
int prefix;

if (!pathspec)
return 0;

path = *pathspec;
slash = strrchr(path, '/');
if (!slash)
return 0;

prefix = slash - path + 1;
while ((next = *++pathspec) != NULL) {
int len = strlen(next);
if (len >= prefix && !memcmp(path, next, len))
continue;
for (;;) {
if (!len)
return 0;
if (next[--len] != '/')
continue;
if (memcmp(path, next, len+1))
continue;
prefix = len + 1;
break;
}
}
return prefix;
}

static int match_one(const char *match, const char *name, int namelen)
{
int matchlen;

/* If the match was just the prefix, we matched */
matchlen = strlen(match);
if (!matchlen)
return 1;

/*
* If we don't match the matchstring exactly,
* we need to match by fnmatch
*/
if (strncmp(match, name, matchlen))
return !fnmatch(match, name, 0);

/*
* If we did match the string exactly, we still
* need to make sure that it happened on a path
* component boundary (ie either the last character
* of the match was '/', or the next character of
* the name was '/' or the terminating NUL.
*/
return match[matchlen-1] == '/' ||
name[matchlen] == '/' ||
!name[matchlen];
}

int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen)
{
int retval;
const char *match;

name += prefix;
namelen -= prefix;

for (retval = 0; (match = *pathspec++) != NULL; seen++) {
if (retval & *seen)
continue;
match += prefix;
if (match_one(match, name, namelen)) {
retval = 1;
*seen = 1;
}
}
return retval;
}

void add_exclude(const char *string, const char *base,
int baselen, struct exclude_list *which)
{
struct exclude *x = xmalloc(sizeof (*x));

x->pattern = string;
x->base = base;
x->baselen = baselen;
if (which->nr == which->alloc) {
which->alloc = alloc_nr(which->alloc);
which->excludes = realloc(which->excludes,
which->alloc * sizeof(x));
}
which->excludes[which->nr++] = x;
}

static int add_excludes_from_file_1(const char *fname,
const char *base,
int baselen,
struct exclude_list *which)
{
int fd, i;
long size;
char *buf, *entry;

fd = open(fname, O_RDONLY);
if (fd < 0)
goto err;
size = lseek(fd, 0, SEEK_END);
if (size < 0)
goto err;
lseek(fd, 0, SEEK_SET);
if (size == 0) {
close(fd);
return 0;
}
buf = xmalloc(size+1);
if (read(fd, buf, size) != size)
goto err;
close(fd);

buf[size++] = '\n';
entry = buf;
for (i = 0; i < size; i++) {
if (buf[i] == '\n') {
if (entry != buf + i && entry[0] != '#') {
buf[i - (i && buf[i-1] == '\r')] = 0;
add_exclude(entry, base, baselen, which);
}
entry = buf + i + 1;
}
}
return 0;

err:
if (0 <= fd)
close(fd);
return -1;
}

void add_excludes_from_file(struct dir_struct *dir, const char *fname)
{
if (add_excludes_from_file_1(fname, "", 0,
&dir->exclude_list[EXC_FILE]) < 0)
die("cannot use %s as an exclude file", fname);
}

static int push_exclude_per_directory(struct dir_struct *dir, const char *base, int baselen)
{
char exclude_file[PATH_MAX];
struct exclude_list *el = &dir->exclude_list[EXC_DIRS];
int current_nr = el->nr;

if (dir->exclude_per_dir) {
memcpy(exclude_file, base, baselen);
strcpy(exclude_file + baselen, dir->exclude_per_dir);
add_excludes_from_file_1(exclude_file, base, baselen, el);
}
return current_nr;
}

static void pop_exclude_per_directory(struct dir_struct *dir, int stk)
{
struct exclude_list *el = &dir->exclude_list[EXC_DIRS];

while (stk < el->nr)
free(el->excludes[--el->nr]);
}

/* Scan the list and let the last match determines the fate.
* Return 1 for exclude, 0 for include and -1 for undecided.
*/
static int excluded_1(const char *pathname,
int pathlen,
struct exclude_list *el)
{
int i;

if (el->nr) {
for (i = el->nr - 1; 0 <= i; i--) {
struct exclude *x = el->excludes[i];
const char *exclude = x->pattern;
int to_exclude = 1;

if (*exclude == '!') {
to_exclude = 0;
exclude++;
}

if (!strchr(exclude, '/')) {
/* match basename */
const char *basename = strrchr(pathname, '/');
basename = (basename) ? basename+1 : pathname;
if (fnmatch(exclude, basename, 0) == 0)
return to_exclude;
}
else {
/* match with FNM_PATHNAME:
* exclude has base (baselen long) implicitly
* in front of it.
*/
int baselen = x->baselen;
if (*exclude == '/')
exclude++;

if (pathlen < baselen ||
(baselen && pathname[baselen-1] != '/') ||
strncmp(pathname, x->base, baselen))
continue;

if (fnmatch(exclude, pathname+baselen,
FNM_PATHNAME) == 0)
return to_exclude;
}
}
}
return -1; /* undecided */
}

int excluded(struct dir_struct *dir, const char *pathname)
{
int pathlen = strlen(pathname);
int st;

for (st = EXC_CMDL; st <= EXC_FILE; st++) {
switch (excluded_1(pathname, pathlen, &dir->exclude_list[st])) {
case 0:
return 0;
case 1:
return 1;
}
}
return 0;
}

static void add_name(struct dir_struct *dir, const char *pathname, int len)
{
struct dir_entry *ent;

if (cache_name_pos(pathname, len) >= 0)
return;

if (dir->nr == dir->alloc) {
int alloc = alloc_nr(dir->alloc);
dir->alloc = alloc;
dir->entries = xrealloc(dir->entries, alloc*sizeof(ent));
}
ent = xmalloc(sizeof(*ent) + len + 1);
ent->len = len;
memcpy(ent->name, pathname, len);
ent->name[len] = 0;
dir->entries[dir->nr++] = ent;
}

static int dir_exists(const char *dirname, int len)
{
int pos = cache_name_pos(dirname, len);
if (pos >= 0)
return 1;
pos = -pos-1;
if (pos >= active_nr) /* can't */
return 0;
return !strncmp(active_cache[pos]->name, dirname, len);
}

/*
* Read a directory tree. We currently ignore anything but
* directories, regular files and symlinks. That's because git
* doesn't handle them at all yet. Maybe that will change some
* day.
*
* Also, we ignore the name ".git" (even if it is not a directory).
* That likely will not change.
*/
static int read_directory_recursive(struct dir_struct *dir, const char *path, const char *base, int baselen)
{
DIR *fdir = opendir(path);
int contents = 0;

if (fdir) {
int exclude_stk;
struct dirent *de;
char fullname[MAXPATHLEN + 1];
memcpy(fullname, base, baselen);

exclude_stk = push_exclude_per_directory(dir, base, baselen);

while ((de = readdir(fdir)) != NULL) {
int len;

if ((de->d_name[0] == '.') &&
(de->d_name[1] == 0 ||
!strcmp(de->d_name + 1, ".") ||
!strcmp(de->d_name + 1, "git")))
continue;
len = strlen(de->d_name);
memcpy(fullname + baselen, de->d_name, len+1);
if (excluded(dir, fullname) != dir->show_ignored) {
if (!dir->show_ignored || DTYPE(de) != DT_DIR) {
continue;
}
}

switch (DTYPE(de)) {
struct stat st;
int subdir, rewind_base;
default:
continue;
case DT_UNKNOWN:
if (lstat(fullname, &st))
continue;
if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
break;
if (!S_ISDIR(st.st_mode))
continue;
/* fallthrough */
case DT_DIR:
memcpy(fullname + baselen + len, "/", 2);
len++;
rewind_base = dir->nr;
subdir = read_directory_recursive(dir, fullname, fullname,
baselen + len);
if (dir->show_other_directories &&
(subdir || !dir->hide_empty_directories) &&
!dir_exists(fullname, baselen + len)) {
// Rewind the read subdirectory
while (dir->nr > rewind_base)
free(dir->entries[--dir->nr]);
break;
}
contents += subdir;
continue;
case DT_REG:
case DT_LNK:
break;
}
add_name(dir, fullname, baselen + len);
contents++;
}
closedir(fdir);

pop_exclude_per_directory(dir, exclude_stk);
}

return contents;
}

static int cmp_name(const void *p1, const void *p2)
{
const struct dir_entry *e1 = *(const struct dir_entry **)p1;
const struct dir_entry *e2 = *(const struct dir_entry **)p2;

return cache_name_compare(e1->name, e1->len,
e2->name, e2->len);
}

int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen)
{
/*
* Make sure to do the per-directory exclude for all the
* directories leading up to our base.
*/
if (baselen) {
if (dir->exclude_per_dir) {
char *p, *pp = xmalloc(baselen+1);
memcpy(pp, base, baselen+1);
p = pp;
while (1) {
char save = *p;
*p = 0;
push_exclude_per_directory(dir, pp, p-pp);
*p++ = save;
if (!save)
break;
p = strchr(p, '/');
if (p)
p++;
else
p = pp + baselen;
}
free(pp);
}
}

read_directory_recursive(dir, path, base, baselen);
qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
return dir->nr;
}

51
dir.h

@ -0,0 +1,51 @@ @@ -0,0 +1,51 @@
#ifndef DIR_H
#define DIR_H

/*
* We maintain three exclude pattern lists:
* EXC_CMDL lists patterns explicitly given on the command line.
* EXC_DIRS lists patterns obtained from per-directory ignore files.
* EXC_FILE lists patterns from fallback ignore files.
*/
#define EXC_CMDL 0
#define EXC_DIRS 1
#define EXC_FILE 2


struct dir_entry {
int len;
char name[FLEX_ARRAY]; /* more */
};

struct exclude_list {
int nr;
int alloc;
struct exclude {
const char *pattern;
const char *base;
int baselen;
} **excludes;
};

struct dir_struct {
int nr, alloc;
unsigned int show_ignored:1,
show_other_directories:1,
hide_empty_directories:1;
struct dir_entry **entries;

/* Exclude info */
const char *exclude_per_dir;
struct exclude_list exclude_list[3];
};

extern int common_prefix(const char **pathspec);
extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen);

extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen);
extern int excluded(struct dir_struct *, const char *);
extern void add_excludes_from_file(struct dir_struct *, const char *fname);
extern void add_exclude(const char *string, const char *base,
int baselen, struct exclude_list *which);

#endif

66
fetch-pack.c

@ -262,22 +262,58 @@ static void mark_recent_complete_commits(unsigned long cutoff) @@ -262,22 +262,58 @@ static void mark_recent_complete_commits(unsigned long cutoff)

static void filter_refs(struct ref **refs, int nr_match, char **match)
{
struct ref *prev, *current, *next;

for (prev = NULL, current = *refs; current; current = next) {
next = current->next;
if ((!memcmp(current->name, "refs/", 5) &&
check_ref_format(current->name + 5)) ||
(!fetch_all &&
!path_match(current->name, nr_match, match))) {
if (prev == NULL)
*refs = next;
else
prev->next = next;
free(current);
} else
prev = current;
struct ref **return_refs;
struct ref *newlist = NULL;
struct ref **newtail = &newlist;
struct ref *ref, *next;
struct ref *fastarray[32];

if (nr_match && !fetch_all) {
if (ARRAY_SIZE(fastarray) < nr_match)
return_refs = xcalloc(nr_match, sizeof(struct ref *));
else {
return_refs = fastarray;
memset(return_refs, 0, sizeof(struct ref *) * nr_match);
}
}
else
return_refs = NULL;

for (ref = *refs; ref; ref = next) {
next = ref->next;
if (!memcmp(ref->name, "refs/", 5) &&
check_ref_format(ref->name + 5))
; /* trash */
else if (fetch_all) {
*newtail = ref;
ref->next = NULL;
newtail = &ref->next;
continue;
}
else {
int order = path_match(ref->name, nr_match, match);
if (order) {
return_refs[order-1] = ref;
continue; /* we will link it later */
}
}
free(ref);
}

if (!fetch_all) {
int i;
for (i = 0; i < nr_match; i++) {
ref = return_refs[i];
if (ref) {
*newtail = ref;
ref->next = NULL;
newtail = &ref->next;
}
}
if (return_refs != fastarray)
free(return_refs);
}
*refs = newlist;
}

static int everything_local(struct ref **refs, int nr_match, char **match)

56
git-add.sh

@ -1,56 +0,0 @@ @@ -1,56 +0,0 @@
#!/bin/sh

USAGE='[-n] [-v] <file>...'
SUBDIRECTORY_OK='Yes'
. git-sh-setup

show_only=
verbose=
while : ; do
case "$1" in
-n)
show_only=true
;;
-v)
verbose=--verbose
;;
--)
shift
break
;;
-*)
usage
;;
*)
break
;;
esac
shift
done

# Check misspelled pathspec
case "$#" in
0) ;;
*)
git-ls-files --error-unmatch --others --cached -- "$@" >/dev/null || {
echo >&2 "Maybe you misspelled it?"
exit 1
}
;;
esac

if test -f "$GIT_DIR/info/exclude"
then
git-ls-files -z \
--exclude-from="$GIT_DIR/info/exclude" \
--others --exclude-per-directory=.gitignore -- "$@"
else
git-ls-files -z \
--others --exclude-per-directory=.gitignore -- "$@"
fi |
case "$show_only" in
true)
xargs -0 echo ;;
*)
git-update-index --add $verbose -z --stdin ;;
esac

344
git-format-patch.sh

@ -1,344 +0,0 @@ @@ -1,344 +0,0 @@
#!/bin/sh
#
# Copyright (c) 2005 Junio C Hamano
#

USAGE='[-n | -k] [-o <dir> | --stdout] [--signoff] [--check] [--diff-options] [--attach] <his> [<mine>]'
LONG_USAGE='Prepare each commit with its patch since <mine> head forked from
<his> head, one file per patch formatted to resemble UNIX mailbox
format, for e-mail submission or use with git-am.

Each output file is numbered sequentially from 1, and uses the
first line of the commit message (massaged for pathname safety)
as the filename.

When -o is specified, output files are created in <dir>; otherwise
they are created in the current working directory. This option
is ignored if --stdout is specified.

When -n is specified, instead of "[PATCH] Subject", the first
line is formatted as "[PATCH N/M] Subject", unless you have only
one patch.

When --attach is specified, patches are attached, not inlined.'

. git-sh-setup

# Force diff to run in C locale.
LANG=C LC_ALL=C
export LANG LC_ALL

diff_opts=
LF='
'

outdir=./
while case "$#" in 0) break;; esac
do
case "$1" in
-c|--c|--ch|--che|--chec|--check)
check=t ;;
-a|--a|--au|--aut|--auth|--autho|--author|\
-d|--d|--da|--dat|--date|\
-m|--m|--mb|--mbo|--mbox) # now noop
;;
--at|--att|--atta|--attac|--attach)
attach=t ;;
-k|--k|--ke|--kee|--keep|--keep-|--keep-s|--keep-su|--keep-sub|\
--keep-subj|--keep-subje|--keep-subjec|--keep-subject)
keep_subject=t ;;
-n|--n|--nu|--num|--numb|--numbe|--number|--numbere|--numbered)
numbered=t ;;
-s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
signoff=t ;;
--st|--std|--stdo|--stdou|--stdout)
stdout=t ;;
-o=*|--o=*|--ou=*|--out=*|--outp=*|--outpu=*|--output=*|--output-=*|\
--output-d=*|--output-di=*|--output-dir=*|--output-dire=*|\
--output-direc=*|--output-direct=*|--output-directo=*|\
--output-director=*|--output-directory=*)
outdir=`expr "$1" : '-[^=]*=\(.*\)'` ;;
-o|--o|--ou|--out|--outp|--outpu|--output|--output-|--output-d|\
--output-di|--output-dir|--output-dire|--output-direc|--output-direct|\
--output-directo|--output-director|--output-directory)
case "$#" in 1) usage ;; esac; shift
outdir="$1" ;;
-h|--h|--he|--hel|--help)
usage
;;
-*' '* | -*"$LF"* | -*' '*)
# Ignore diff option that has whitespace for now.
;;
-*) diff_opts="$diff_opts$1 " ;;
*) break ;;
esac
shift
done

case "$keep_subject$numbered" in
tt)
die '--keep-subject and --numbered are incompatible.' ;;
esac

tmp=.tmp-series$$
trap 'rm -f $tmp-*' 0 1 2 3 15

series=$tmp-series
commsg=$tmp-commsg
filelist=$tmp-files

# Backward compatible argument parsing hack.
#
# Historically, we supported:
# 1. "rev1" is equivalent to "rev1..HEAD"
# 2. "rev1..rev2"
# 3. "rev1" "rev2 is equivalent to "rev1..rev2"
#
# We want to take a sequence of "rev1..rev2" in general.
# Also, "rev1.." should mean "rev1..HEAD"; git-diff users are
# familiar with that syntax.

case "$#,$1$2" in
1,?*..?*)
# single "rev1..rev2"
;;
1,?*..)
# single "rev1.." should mean "rev1..HEAD"
set x "$1"HEAD
shift
;;
1,*)
# single rev1
set x "$1..HEAD"
shift
;;
2,?*..?*)
# not traditional "rev1" "rev2"
;;
2,*)
set x "$1..$2"
shift
;;
esac

# Now we have what we want in $@
for revpair
do
case "$revpair" in
?*..?*)
rev1=`expr "z$revpair" : 'z\(.*\)\.\.'`
rev2=`expr "z$revpair" : 'z.*\.\.\(.*\)'`
;;
*)
rev1="$revpair^"
rev2="$revpair"
;;
esac
git-rev-parse --verify "$rev1^0" >/dev/null 2>&1 ||
die "Not a valid rev $rev1 ($revpair)"
git-rev-parse --verify "$rev2^0" >/dev/null 2>&1 ||
die "Not a valid rev $rev2 ($revpair)"
git-cherry -v "$rev1" "$rev2" |
while read sign rev comment
do
case "$sign" in
'-')
echo >&2 "Merged already: $comment"
;;
*)
echo $rev
;;
esac
done
done >$series

me=`git-var GIT_AUTHOR_IDENT | sed -e 's/>.*/>/'`
headers=`git-repo-config --get format.headers`
case "$attach" in
"") ;;
*)
mimemagic="050802040500080604070107"
esac

case "$outdir" in
*/) ;;
*) outdir="$outdir/" ;;
esac
test -d "$outdir" || mkdir -p "$outdir" || exit

titleScript='
/./d
/^$/n
s/^\[PATCH[^]]*\] *//
s/[^-a-z.A-Z_0-9]/-/g
s/\.\.\.*/\./g
s/\.*$//
s/--*/-/g
s/^-//
s/-$//
s/$/./
p
q
'

process_one () {
perl -w -e '
my ($keep_subject, $num, $signoff, $headers, $mimemagic, $commsg) = @ARGV;
my ($signoff_pattern, $done_header, $done_subject, $done_separator, $signoff_seen,
$last_was_signoff);

if ($signoff) {
$signoff = "Signed-off-by: " . `git-var GIT_COMMITTER_IDENT`;
$signoff =~ s/>.*/>/;
$signoff_pattern = quotemeta($signoff);
}

my @weekday_names = qw(Sun Mon Tue Wed Thu Fri Sat);
my @month_names = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);

sub show_date {
my ($time, $tz) = @_;
my $minutes = abs($tz);
$minutes = int($minutes / 100) * 60 + ($minutes % 100);
if ($tz < 0) {
$minutes = -$minutes;
}
my $t = $time + $minutes * 60;
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime($t);
return sprintf("%s, %d %s %d %02d:%02d:%02d %+05d",
$weekday_names[$wday], $mday,
$month_names[$mon], $year+1900,
$hour, $min, $sec, $tz);
}

print "From nobody Mon Sep 17 00:00:00 2001\n";
open FH, "git stripspace <$commsg |" or die "open $commsg pipe";
while (<FH>) {
unless ($done_header) {
if (/^$/) {
$done_header = 1;
}
elsif (/^author (.*>) (.*)$/) {
my ($author_ident, $author_date) = ($1, $2);
my ($utc, $off) = ($author_date =~ /^(\d+) ([-+]?\d+)$/);
$author_date = show_date($utc, $off);

print "From: $author_ident\n";
print "Date: $author_date\n";
}
next;
}
unless ($done_subject) {
unless ($keep_subject) {
s/^\[PATCH[^]]*\]\s*//;
s/^/[PATCH$num] /;
}
if ($headers) {
print "$headers\n";
}
print "Subject: $_";
if ($mimemagic) {
print "MIME-Version: 1.0\n";
print "Content-Type: multipart/mixed;\n";
print " boundary=\"------------$mimemagic\"\n";
print "\n";
print "This is a multi-part message in MIME format.\n";
print "--------------$mimemagic\n";
print "Content-Type: text/plain; charset=UTF-8; format=fixed\n";
print "Content-Transfer-Encoding: 8bit\n";
}
$done_subject = 1;
next;
}
unless ($done_separator) {
print "\n";
$done_separator = 1;
next if (/^$/);
}

$last_was_signoff = 0;
if (/Signed-off-by:/i) {
if ($signoff ne "" && /Signed-off-by:\s*$signoff_pattern$/i) {
$signoff_seen = 1;
}
}
print $_;
}
if (!$signoff_seen && $signoff ne "") {
if (!$last_was_signoff) {
print "\n";
}
print "$signoff\n";
}
print "\n---\n\n";
close FH or die "close $commsg pipe";
' "$keep_subject" "$num" "$signoff" "$headers" "$mimemagic" $commsg

git-diff-tree -p --stat --summary $diff_opts "$commit"
echo
case "$mimemagic" in
'');;
*)
echo "--------------$mimemagic"
echo "Content-Type: text/x-patch;"
echo " name=\"$commit.diff\""
echo "Content-Transfer-Encoding: 8bit"
echo "Content-Disposition: inline;"
echo " filename=\"$commit.diff\""
echo
esac
git-diff-tree -p $diff_opts "$commit"
case "$mimemagic" in
'')
echo "-- "
echo "@@GIT_VERSION@@"
;;
*)
echo
echo "--------------$mimemagic--"
echo
;;
esac
echo
}

total=`wc -l <$series | tr -dc "[0-9]"`
case "$total,$numbered" in
1,*)
numfmt='' ;;
*,t)
numfmt=`echo "$total" | wc -c`
numfmt=$(($numfmt-1))
numfmt=" %0${numfmt}d/$total"
esac

i=1
while read commit
do
git-cat-file commit "$commit" | git-stripspace >$commsg
title=`sed -ne "$titleScript" <$commsg`
case "$numbered" in
'') num= ;;
*)
num=`printf "$numfmt" $i` ;;
esac

file=`printf '%04d-%stxt' $i "$title"`
if test '' = "$stdout"
then
echo "$file"
process_one >"$outdir$file"
if test t = "$check"
then
# This is slightly modified from Andrew Morton's Perfect Patch.
# Lines you introduce should not have trailing whitespace.
# Also check for an indentation that has SP before a TAB.
grep -n '^+\([ ]* .*\|.*[ ]\)$' "$outdir$file"
:
fi
else
echo >&2 "$file"
process_one
fi
i=`expr "$i" + 1`
done <$series

2
git-rebase.sh

@ -152,6 +152,6 @@ then @@ -152,6 +152,6 @@ then
exit 0
fi

git-format-patch -k --stdout --full-index "$upstream" ORIG_HEAD |
git-format-patch -k --stdout --full-index "$upstream"..ORIG_HEAD |
git am --binary -3 -k --resolvemsg="$RESOLVEMSG"


70
git-rm.sh

@ -1,70 +0,0 @@ @@ -1,70 +0,0 @@
#!/bin/sh

USAGE='[-f] [-n] [-v] [--] <file>...'
SUBDIRECTORY_OK='Yes'
. git-sh-setup

remove_files=
show_only=
verbose=
while : ; do
case "$1" in
-f)
remove_files=true
;;
-n)
show_only=true
;;
-v)
verbose=--verbose
;;
--)
shift; break
;;
-*)
usage
;;
*)
break
;;
esac
shift
done

# This is typo-proofing. If some paths match and some do not, we want
# to do nothing.
case "$#" in
0) ;;
*)
git-ls-files --error-unmatch -- "$@" >/dev/null || {
echo >&2 "Maybe you misspelled it?"
exit 1
}
;;
esac

if test -f "$GIT_DIR/info/exclude"
then
git-ls-files -z \
--exclude-from="$GIT_DIR/info/exclude" \
--exclude-per-directory=.gitignore -- "$@"
else
git-ls-files -z \
--exclude-per-directory=.gitignore -- "$@"
fi |
case "$show_only,$remove_files" in
true,*)
xargs -0 echo
;;
*,true)
xargs -0 sh -c "
while [ \$# -gt 0 ]; do
file=\$1; shift
rm -- \"\$file\" && git-update-index --remove $verbose \"\$file\"
done
" inline
;;
*)
git-update-index --force-remove $verbose -z --stdin
;;
esac

19
git.c

@ -47,12 +47,29 @@ static void handle_internal_command(int argc, const char **argv, char **envp) @@ -47,12 +47,29 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "whatchanged", cmd_whatchanged },
{ "show", cmd_show },
{ "push", cmd_push },
{ "format-patch", cmd_format_patch },
{ "count-objects", cmd_count_objects },
{ "diff", cmd_diff },
{ "grep", cmd_grep },
{ "rm", cmd_rm },
{ "add", cmd_add },
{ "rev-list", cmd_rev_list },
{ "init-db", cmd_init_db },
{ "check-ref-format", cmd_check_ref_format }
{ "tar-tree", cmd_tar_tree },
{ "upload-tar", cmd_upload_tar },
{ "check-ref-format", cmd_check_ref_format },
{ "ls-files", cmd_ls_files },
{ "ls-tree", cmd_ls_tree },
{ "tar-tree", cmd_tar_tree },
{ "read-tree", cmd_read_tree },
{ "commit-tree", cmd_commit_tree },
{ "apply", cmd_apply },
{ "show-branch", cmd_show_branch },
{ "diff-files", cmd_diff_files },
{ "diff-index", cmd_diff_index },
{ "diff-stages", cmd_diff_stages },
{ "diff-tree", cmd_diff_tree },
{ "cat-file", cmd_cat_file }
};
int i;


74
log-tree.c

@ -20,6 +20,7 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep) @@ -20,6 +20,7 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep)
int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40;
const char *extra;
int len;
char *subject = NULL, *after_subject = NULL;

opt->loginfo = NULL;
if (!opt->verbose_header) {
@ -49,19 +50,67 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep) @@ -49,19 +50,67 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep)
/*
* Print header line of header..
*/
printf("%s%s",
opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ",
diff_unique_abbrev(commit->object.sha1, abbrev_commit));
if (opt->parents)
show_parents(commit, abbrev_commit);
if (parent)
printf(" (from %s)", diff_unique_abbrev(parent->object.sha1, abbrev_commit));
putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n');

if (opt->commit_format == CMIT_FMT_EMAIL) {
char *sha1 = sha1_to_hex(commit->object.sha1);
if (opt->total > 0) {
static char buffer[64];
snprintf(buffer, sizeof(buffer),
"Subject: [PATCH %d/%d] ",
opt->nr, opt->total);
subject = buffer;
} else if (opt->total == 0)
subject = "Subject: [PATCH] ";
else
subject = "Subject: ";

printf("From %s Mon Sep 17 00:00:00 2001\n", sha1);
if (opt->mime_boundary) {
static char subject_buffer[1024];
static char buffer[1024];
snprintf(subject_buffer, sizeof(subject_buffer) - 1,
"MIME-Version: 1.0\n"
"Content-Type: multipart/mixed;\n"
" boundary=\"%s%s\"\n"
"\n"
"This is a multi-part message in MIME "
"format.\n"
"--%s%s\n"
"Content-Type: text/plain; "
"charset=UTF-8; format=fixed\n"
"Content-Transfer-Encoding: 8bit\n\n",
mime_boundary_leader, opt->mime_boundary,
mime_boundary_leader, opt->mime_boundary);
after_subject = subject_buffer;

snprintf(buffer, sizeof(buffer) - 1,
"--%s%s\n"
"Content-Type: text/x-patch;\n"
" name=\"%s.diff\"\n"
"Content-Transfer-Encoding: 8bit\n"
"Content-Disposition: inline;\n"
" filename=\"%s.diff\"\n\n",
mime_boundary_leader, opt->mime_boundary,
sha1, sha1);
opt->diffopt.stat_sep = buffer;
}
} else {
printf("%s%s",
opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ",
diff_unique_abbrev(commit->object.sha1, abbrev_commit));
if (opt->parents)
show_parents(commit, 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);
len = pretty_print_commit(opt->commit_format, commit, ~0u, this_header, sizeof(this_header), abbrev, subject, after_subject);
printf("%s%s%s", this_header, extra, sep);
}

@ -166,15 +215,18 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log @@ -166,15 +215,18 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
int log_tree_commit(struct rev_info *opt, struct commit *commit)
{
struct log_info log;
int shown;

log.commit = commit;
log.parent = NULL;
opt->loginfo = &log;

if (!log_tree_diff(opt, commit, &log) && opt->loginfo && opt->always_show_header) {
shown = log_tree_diff(opt, commit, &log);
if (!shown && opt->loginfo && opt->always_show_header) {
log.parent = NULL;
show_log(opt, opt->loginfo, "");
shown = 1;
}
opt->loginfo = NULL;
return 0;
return shown;
}

45
mktag.c

@ -45,42 +45,46 @@ static int verify_tag(char *buffer, unsigned long size) @@ -45,42 +45,46 @@ static int verify_tag(char *buffer, unsigned long size)
unsigned char sha1[20];
const char *object, *type_line, *tag_line, *tagger_line;

if (size < 64 || size > MAXSIZE-1)
return -1;
if (size < 64)
return error("wanna fool me ? you obviously got the size wrong !\n");

buffer[size] = 0;

/* Verify object line */
object = buffer;
if (memcmp(object, "object ", 7))
return -1;
return error("char%d: does not start with \"object \"\n", 0);

if (get_sha1_hex(object + 7, sha1))
return -1;
return error("char%d: could not get SHA1 hash\n", 7);

/* Verify type line */
type_line = object + 48;
if (memcmp(type_line - 1, "\ntype ", 6))
return -1;
return error("char%d: could not find \"\\ntype \"\n", 47);

/* Verify tag-line */
tag_line = strchr(type_line, '\n');
if (!tag_line)
return -1;
return error("char%td: could not find next \"\\n\"\n", type_line - buffer);
tag_line++;
if (memcmp(tag_line, "tag ", 4) || tag_line[4] == '\n')
return -1;
return error("char%td: no \"tag \" found\n", tag_line - buffer);

/* Get the actual type */
typelen = tag_line - type_line - strlen("type \n");
if (typelen >= sizeof(type))
return -1;
return error("char%td: type too long\n", type_line+5 - buffer);

memcpy(type, type_line+5, typelen);
type[typelen] = 0;

/* Verify that the object matches */
if (get_sha1_hex(object + 7, sha1))
return -1;
return error("char%d: could not get SHA1 hash but this is really odd since i got it before !\n", 7);

if (verify_object(sha1, type))
return -1;
return error("char%d: could not verify object %s\n", 7, sha1);

/* Verify the tag-name: we don't allow control characters or spaces in it */
tag_line += 4;
@ -90,14 +94,14 @@ static int verify_tag(char *buffer, unsigned long size) @@ -90,14 +94,14 @@ static int verify_tag(char *buffer, unsigned long size)
break;
if (c > ' ')
continue;
return -1;
return error("char%td: could not verify tag name\n", tag_line - buffer);
}

/* Verify the tagger line */
tagger_line = tag_line;

if (memcmp(tagger_line, "tagger", 6) || (tagger_line[6] == '\n'))
return -1;
return error("char%td: could not find \"tagger\"\n", tagger_line - buffer);

/* The actual stuff afterwards we don't care about.. */
return 0;
@ -105,8 +109,8 @@ static int verify_tag(char *buffer, unsigned long size) @@ -105,8 +109,8 @@ static int verify_tag(char *buffer, unsigned long size)

int main(int argc, char **argv)
{
unsigned long size;
char buffer[MAXSIZE];
unsigned long size = 4096;
char *buffer = malloc(size);
unsigned char result_sha1[20];

if (argc != 1)
@ -114,13 +118,9 @@ int main(int argc, char **argv) @@ -114,13 +118,9 @@ int main(int argc, char **argv)

setup_git_directory();

// Read the signature
size = 0;
for (;;) {
int ret = xread(0, buffer + size, MAXSIZE - size);
if (ret <= 0)
break;
size += ret;
if (read_pipe(0, &buffer, &size)) {
free(buffer);
die("could not read from stdin");
}

// Verify it for some basic sanity: it needs to start with "object <sha1>\ntype\ntagger "
@ -129,6 +129,9 @@ int main(int argc, char **argv) @@ -129,6 +129,9 @@ int main(int argc, char **argv)

if (write_sha1_file(buffer, size, tag_type, result_sha1) < 0)
die("unable to write tag file");

free(buffer);

printf("%s\n", sha1_to_hex(result_sha1));
return 0;
}

66
read-cache.c

@ -331,6 +331,70 @@ int ce_path_match(const struct cache_entry *ce, const char **pathspec) @@ -331,6 +331,70 @@ int ce_path_match(const struct cache_entry *ce, const char **pathspec)
return 0;
}

/*
* We fundamentally don't like some paths: we don't want
* dot or dot-dot anywhere, and for obvious reasons don't
* want to recurse into ".git" either.
*
* Also, we don't want double slashes or slashes at the
* end that can make pathnames ambiguous.
*/
static int verify_dotfile(const char *rest)
{
/*
* The first character was '.', but that
* has already been discarded, we now test
* the rest.
*/
switch (*rest) {
/* "." is not allowed */
case '\0': case '/':
return 0;

/*
* ".git" followed by NUL or slash is bad. This
* shares the path end test with the ".." case.
*/
case 'g':
if (rest[1] != 'i')
break;
if (rest[2] != 't')
break;
rest += 2;
/* fallthrough */
case '.':
if (rest[1] == '\0' || rest[1] == '/')
return 0;
}
return 1;
}

int verify_path(const char *path)
{
char c;

goto inside;
for (;;) {
if (!c)
return 1;
if (c == '/') {
inside:
c = *path++;
switch (c) {
default:
continue;
case '/': case '\0':
break;
case '.':
if (verify_dotfile(path))
continue;
}
return 0;
}
c = *path++;
}
}

/*
* Do we have another file that has the beginning components being a
* proper superset of the name we're trying to add?
@ -472,6 +536,8 @@ int add_cache_entry(struct cache_entry *ce, int option) @@ -472,6 +536,8 @@ int add_cache_entry(struct cache_entry *ce, int option)

if (!ok_to_add)
return -1;
if (!verify_path(ce->name))
return -1;

if (!skip_df_check &&
check_file_directory_conflict(ce, pos, ok_to_replace)) {

2
revision.h

@ -58,6 +58,8 @@ struct rev_info { @@ -58,6 +58,8 @@ struct rev_info {
unsigned int abbrev;
enum cmit_fmt commit_format;
struct log_info *loginfo;
int nr, total;
const char *mime_boundary;

/* special limits */
int max_count;

185
sha1_file.c

@ -1399,6 +1399,25 @@ int move_temp_to_file(const char *tmpfile, char *filename) @@ -1399,6 +1399,25 @@ int move_temp_to_file(const char *tmpfile, char *filename)
return 0;
}

static int write_buffer(int fd, const void *buf, size_t len)
{
while (len) {
ssize_t size;

size = write(fd, buf, len);
if (!size)
return error("file write: disk full");
if (size < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
return error("file write error (%s)", strerror(errno));
}
len -= size;
buf += size;
}
return 0;
}

int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
{
int size;
@ -1465,8 +1484,8 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha @@ -1465,8 +1484,8 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
deflateEnd(&stream);
size = stream.total_out;

if (write(fd, compressed, size) != size)
die("unable to write file");
if (write_buffer(fd, compressed, size) < 0)
die("unable to write sha1 file");
fchmod(fd, 0444);
close(fd);
free(compressed);
@ -1474,73 +1493,70 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha @@ -1474,73 +1493,70 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
return move_temp_to_file(tmpfile, filename);
}

int write_sha1_to_fd(int fd, const unsigned char *sha1)
/*
* We need to unpack and recompress the object for writing
* it out to a different file.
*/
static void *repack_object(const unsigned char *sha1, unsigned long *objsize)
{
ssize_t size;
unsigned long objsize;
int posn = 0;
void *map = map_sha1_file_internal(sha1, &objsize);
void *buf = map;
void *temp_obj = NULL;
size_t size;
z_stream stream;
unsigned char *unpacked;
unsigned long len;
char type[20];
char hdr[50];
int hdrlen;
void *buf;

if (!buf) {
unsigned char *unpacked;
unsigned long len;
char type[20];
char hdr[50];
int hdrlen;
// need to unpack and recompress it by itself
unpacked = read_packed_sha1(sha1, type, &len);
// need to unpack and recompress it by itself
unpacked = read_packed_sha1(sha1, type, &len);

hdrlen = sprintf(hdr, "%s %lu", type, len) + 1;
hdrlen = sprintf(hdr, "%s %lu", type, len) + 1;

/* Set it up */
memset(&stream, 0, sizeof(stream));
deflateInit(&stream, Z_BEST_COMPRESSION);
size = deflateBound(&stream, len + hdrlen);
temp_obj = buf = xmalloc(size);

/* Compress it */
stream.next_out = buf;
stream.avail_out = size;
/* First header.. */
stream.next_in = (void *)hdr;
stream.avail_in = hdrlen;
while (deflate(&stream, 0) == Z_OK)
/* nothing */;
/* Set it up */
memset(&stream, 0, sizeof(stream));
deflateInit(&stream, Z_BEST_COMPRESSION);
size = deflateBound(&stream, len + hdrlen);
buf = xmalloc(size);

/* Then the data itself.. */
stream.next_in = unpacked;
stream.avail_in = len;
while (deflate(&stream, Z_FINISH) == Z_OK)
/* nothing */;
deflateEnd(&stream);
free(unpacked);
objsize = stream.total_out;
}
/* Compress it */
stream.next_out = buf;
stream.avail_out = size;

do {
size = write(fd, buf + posn, objsize - posn);
if (size <= 0) {
if (!size) {
fprintf(stderr, "write closed\n");
} else {
perror("write ");
}
return -1;
}
posn += size;
} while (posn < objsize);
/* First header.. */
stream.next_in = (void *)hdr;
stream.avail_in = hdrlen;
while (deflate(&stream, 0) == Z_OK)
/* nothing */;

if (map)
munmap(map, objsize);
if (temp_obj)
free(temp_obj);
/* Then the data itself.. */
stream.next_in = unpacked;
stream.avail_in = len;
while (deflate(&stream, Z_FINISH) == Z_OK)
/* nothing */;
deflateEnd(&stream);
free(unpacked);

return 0;
*objsize = stream.total_out;
return buf;
}

int write_sha1_to_fd(int fd, const unsigned char *sha1)
{
int retval;
unsigned long objsize;
void *buf = map_sha1_file_internal(sha1, &objsize);

if (buf) {
retval = write_buffer(fd, buf, objsize);
munmap(buf, objsize);
return retval;
}

buf = repack_object(sha1, &objsize);
retval = write_buffer(fd, buf, objsize);
free(buf);
return retval;
}

int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
@ -1579,7 +1595,8 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer, @@ -1579,7 +1595,8 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
SHA1_Update(&c, discard, sizeof(discard) -
stream.avail_out);
} while (stream.avail_in && ret == Z_OK);
write(local, buffer, *bufposn - stream.avail_in);
if (write_buffer(local, buffer, *bufposn - stream.avail_in) < 0)
die("unable to write sha1 file");
memmove(buffer, buffer + *bufposn - stream.avail_in,
stream.avail_in);
*bufposn = stream.avail_in;
@ -1645,16 +1662,24 @@ int has_sha1_file(const unsigned char *sha1) @@ -1645,16 +1662,24 @@ int has_sha1_file(const unsigned char *sha1)
return find_sha1_file(sha1, &st) ? 1 : 0;
}

int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
/*
* reads from fd as long as possible into a supplied buffer of size bytes.
* If neccessary the buffer's size is increased using realloc()
*
* returns 0 if anything went fine and -1 otherwise
*
* NOTE: both buf and size may change, but even when -1 is returned
* you still have to free() it yourself.
*/
int read_pipe(int fd, char** return_buf, unsigned long* return_size)
{
unsigned long size = 4096;
char *buf = malloc(size);
int iret, ret;
char* buf = *return_buf;
unsigned long size = *return_size;
int iret;
unsigned long off = 0;
unsigned char hdr[50];
int hdrlen;

do {
iret = read(fd, buf + off, size - off);
iret = xread(fd, buf + off, size - off);
if (iret > 0) {
off += iret;
if (off == size) {
@ -1663,16 +1688,34 @@ int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object) @@ -1663,16 +1688,34 @@ int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
}
}
} while (iret > 0);
if (iret < 0) {

*return_buf = buf;
*return_size = off;

if (iret < 0)
return -1;
return 0;
}

int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
{
unsigned long size = 4096;
char *buf = malloc(size);
int ret;
unsigned char hdr[50];
int hdrlen;

if (read_pipe(fd, &buf, &size)) {
free(buf);
return -1;
}

if (!type)
type = blob_type;
if (write_object)
ret = write_sha1_file(buf, off, type, sha1);
ret = write_sha1_file(buf, size, type, sha1);
else {
write_sha1_file_prepare(buf, off, type, sha1, hdr, &hdrlen);
write_sha1_file_prepare(buf, size, type, sha1, hdr, &hdrlen);
ret = 0;
}
free(buf);

64
update-index.c

@ -120,70 +120,6 @@ static int add_file_to_cache(const char *path) @@ -120,70 +120,6 @@ static int add_file_to_cache(const char *path)
return 0;
}

/*
* We fundamentally don't like some paths: we don't want
* dot or dot-dot anywhere, and for obvious reasons don't
* want to recurse into ".git" either.
*
* Also, we don't want double slashes or slashes at the
* end that can make pathnames ambiguous.
*/
static int verify_dotfile(const char *rest)
{
/*
* The first character was '.', but that
* has already been discarded, we now test
* the rest.
*/
switch (*rest) {
/* "." is not allowed */
case '\0': case '/':
return 0;

/*
* ".git" followed by NUL or slash is bad. This
* shares the path end test with the ".." case.
*/
case 'g':
if (rest[1] != 'i')
break;
if (rest[2] != 't')
break;
rest += 2;
/* fallthrough */
case '.':
if (rest[1] == '\0' || rest[1] == '/')
return 0;
}
return 1;
}

static int verify_path(const char *path)
{
char c;

goto inside;
for (;;) {
if (!c)
return 1;
if (c == '/') {
inside:
c = *path++;
switch (c) {
default:
continue;
case '/': case '\0':
break;
case '.':
if (verify_dotfile(path))
continue;
}
return 0;
}
c = *path++;
}
}

static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
const char *path, int stage)
{

Loading…
Cancel
Save