From fae22ac9d7b5fd8bbf0fcfb01aab01c27d84912f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 22 Jun 2005 02:30:47 -0700 Subject: [PATCH] git-apply: tests for --stat and --summary. This adds tests (which also serves demonstration) for the --stat and --summary flags to the git-apply command. Signed-off-by: Junio C Hamano Signed-off-by: Linus Torvalds diff --git a/t/t4100-apply-stat.sh b/t/t4100-apply-stat.sh new file mode 100644 index 0000000..6579f06 --- /dev/null +++ b/t/t4100-apply-stat.sh @@ -0,0 +1,47 @@ +#!/bin/sh +# +# Copyright (c) 2005 Junio C Hamano +# + +test_description='git-apply --stat --summary test. + +' +. ./test-lib.sh + +test_expect_success \ + 'rename' \ + 'git-apply --stat --summary <../t4100/t-apply-1.patch >current && + diff -u ../t4100/t-apply-1.expect current' + +test_expect_success \ + 'copy' \ + 'git-apply --stat --summary <../t4100/t-apply-2.patch >current && + diff -u ../t4100/t-apply-2.expect current' + +test_expect_success \ + 'rewrite' \ + 'git-apply --stat --summary <../t4100/t-apply-3.patch >current && + diff -u ../t4100/t-apply-3.expect current' + +test_expect_success \ + 'mode' \ + 'git-apply --stat --summary <../t4100/t-apply-4.patch >current && + diff -u ../t4100/t-apply-4.expect current' + +test_expect_success \ + 'non git' \ + 'git-apply --stat --summary <../t4100/t-apply-5.patch >current && + diff -u ../t4100/t-apply-5.expect current' + +test_expect_success \ + 'non git' \ + 'git-apply --stat --summary <../t4100/t-apply-6.patch >current && + diff -u ../t4100/t-apply-6.expect current' + +test_expect_success \ + 'non git' \ + 'git-apply --stat --summary <../t4100/t-apply-7.patch >current && + diff -u ../t4100/t-apply-7.expect current' + +test_done + diff --git a/t/t4100/t-apply-1.expect b/t/t4100/t-apply-1.expect new file mode 100644 index 0000000..540e64d --- /dev/null +++ b/t/t4100/t-apply-1.expect @@ -0,0 +1,11 @@ + Documentation/git-ssh-pull.txt | 12 ++++++------ + Documentation/git-ssh-push.txt | 10 +++++----- + Documentation/git.txt | 6 +++--- + Makefile | 6 +++--- + ssh-pull.c | 4 ++-- + ssh-push.c | 14 +++++++------- + 6 files changed, 26 insertions(+), 26 deletions(-) + rename Documentation/{git-rpull.txt => git-ssh-pull.txt} (90%) + rename Documentation/{git-rpush.txt => git-ssh-push.txt} (71%) + rename rpull.c => ssh-pull.c (97%) + rename rpush.c => ssh-push.c (93%) diff --git a/t/t4100/t-apply-1.patch b/t/t4100/t-apply-1.patch new file mode 100644 index 0000000..de58751 --- /dev/null +++ b/t/t4100/t-apply-1.patch @@ -0,0 +1,194 @@ +418aaf847a8b3ffffb4f777a2dd5262ca5ce0ef7 (from dc93841715dfa9a9cdda6f2c4a25eec831ea7aa0) +diff --git a/Documentation/git-rpull.txt b/Documentation/git-ssh-pull.txt +similarity index 90% +rename from Documentation/git-rpull.txt +rename to Documentation/git-ssh-pull.txt +--- a/Documentation/git-rpull.txt ++++ b/Documentation/git-ssh-pull.txt +@@ -1,21 +1,21 @@ +-git-rpull(1) +-============ ++git-ssh-pull(1) ++=============== + v0.1, May 2005 + + NAME + ---- +-git-rpull - Pulls from a remote repository over ssh connection ++git-ssh-pull - Pulls from a remote repository over ssh connection + + + + SYNOPSIS + -------- +-'git-rpull' [-c] [-t] [-a] [-d] [-v] [--recover] commit-id url ++'git-ssh-pull' [-c] [-t] [-a] [-d] [-v] [--recover] commit-id url + + DESCRIPTION + ----------- +-Pulls from a remote repository over ssh connection, invoking git-rpush on +-the other end. ++Pulls from a remote repository over ssh connection, invoking git-ssh-push ++on the other end. + + OPTIONS + ------- +diff --git a/Documentation/git-rpush.txt b/Documentation/git-ssh-push.txt +similarity index 71% +rename from Documentation/git-rpush.txt +rename to Documentation/git-ssh-push.txt +--- a/Documentation/git-rpush.txt ++++ b/Documentation/git-ssh-push.txt +@@ -1,19 +1,19 @@ +-git-rpush(1) +-============ ++git-ssh-push(1) ++=============== + v0.1, May 2005 + + NAME + ---- +-git-rpush - Helper "server-side" program used by git-rpull ++git-ssh-push - Helper "server-side" program used by git-ssh-pull + + + SYNOPSIS + -------- +-'git-rpush' ++'git-ssh-push' + + DESCRIPTION + ----------- +-Helper "server-side" program used by git-rpull. ++Helper "server-side" program used by git-ssh-pull. + + + Author +diff --git a/Documentation/git.txt b/Documentation/git.txt +--- a/Documentation/git.txt ++++ b/Documentation/git.txt +@@ -148,7 +148,7 @@ link:git-resolve-script.html[git-resolve + link:git-tag-script.html[git-tag-script]:: + An example script to create a tag object signed with GPG + +-link:git-rpull.html[git-rpull]:: ++link:git-ssh-pull.html[git-ssh-pull]:: + Pulls from a remote repository over ssh connection + + Interogators: +@@ -156,8 +156,8 @@ Interogators: + link:git-diff-helper.html[git-diff-helper]:: + Generates patch format output for git-diff-* + +-link:git-rpush.html[git-rpush]:: +- Helper "server-side" program used by git-rpull ++link:git-ssh-push.html[git-ssh-push]:: ++ Helper "server-side" program used by git-ssh-pull + + + +diff --git a/Makefile b/Makefile +--- a/Makefile ++++ b/Makefile +@@ -30,7 +30,7 @@ PROG= git-update-cache git-diff-files + git-checkout-cache git-diff-tree git-rev-tree git-ls-files \ + git-check-files git-ls-tree git-merge-base git-merge-cache \ + git-unpack-file git-export git-diff-cache git-convert-cache \ +- git-http-pull git-rpush git-rpull git-rev-list git-mktag \ ++ git-http-pull git-ssh-push git-ssh-pull git-rev-list git-mktag \ + git-diff-helper git-tar-tree git-local-pull git-write-blob \ + git-get-tar-commit-id git-mkdelta git-apply git-stripspace + +@@ -105,8 +105,8 @@ git-diff-cache: diff-cache.c + git-convert-cache: convert-cache.c + git-http-pull: http-pull.c pull.c + git-local-pull: local-pull.c pull.c +-git-rpush: rsh.c +-git-rpull: rsh.c pull.c ++git-ssh-push: rsh.c ++git-ssh-pull: rsh.c pull.c + git-rev-list: rev-list.c + git-mktag: mktag.c + git-diff-helper: diff-helper.c +diff --git a/rpull.c b/ssh-pull.c +similarity index 97% +rename from rpull.c +rename to ssh-pull.c +--- a/rpull.c ++++ b/ssh-pull.c +@@ -64,13 +64,13 @@ int main(int argc, char **argv) + arg++; + } + if (argc < arg + 2) { +- usage("git-rpull [-c] [-t] [-a] [-v] [-d] [--recover] commit-id url"); ++ usage("git-ssh-pull [-c] [-t] [-a] [-v] [-d] [--recover] commit-id url"); + return 1; + } + commit_id = argv[arg]; + url = argv[arg + 1]; + +- if (setup_connection(&fd_in, &fd_out, "git-rpush", url, arg, argv + 1)) ++ if (setup_connection(&fd_in, &fd_out, "git-ssh-push", url, arg, argv + 1)) + return 1; + + if (get_version()) +diff --git a/rpush.c b/ssh-push.c +similarity index 93% +rename from rpush.c +rename to ssh-push.c +--- a/rpush.c ++++ b/ssh-push.c +@@ -16,7 +16,7 @@ int serve_object(int fd_in, int fd_out) + do { + size = read(fd_in, sha1 + posn, 20 - posn); + if (size < 0) { +- perror("git-rpush: read "); ++ perror("git-ssh-push: read "); + return -1; + } + if (!size) +@@ -30,7 +30,7 @@ int serve_object(int fd_in, int fd_out) + buf = map_sha1_file(sha1, &objsize); + + if (!buf) { +- fprintf(stderr, "git-rpush: could not find %s\n", ++ fprintf(stderr, "git-ssh-push: could not find %s\n", + sha1_to_hex(sha1)); + remote = -1; + } +@@ -45,9 +45,9 @@ int serve_object(int fd_in, int fd_out) + size = write(fd_out, buf + posn, objsize - posn); + if (size <= 0) { + if (!size) { +- fprintf(stderr, "git-rpush: write closed"); ++ fprintf(stderr, "git-ssh-push: write closed"); + } else { +- perror("git-rpush: write "); ++ perror("git-ssh-push: write "); + } + return -1; + } +@@ -71,7 +71,7 @@ void service(int fd_in, int fd_out) { + retval = read(fd_in, &type, 1); + if (retval < 1) { + if (retval < 0) +- perror("rpush: read "); ++ perror("git-ssh-push: read "); + return; + } + if (type == 'v' && serve_version(fd_in, fd_out)) +@@ -91,12 +91,12 @@ int main(int argc, char **argv) + arg++; + } + if (argc < arg + 2) { +- usage("git-rpush [-c] [-t] [-a] commit-id url"); ++ usage("git-ssh-push [-c] [-t] [-a] commit-id url"); + return 1; + } + commit_id = argv[arg]; + url = argv[arg + 1]; +- if (setup_connection(&fd_in, &fd_out, "git-rpull", url, arg, argv + 1)) ++ if (setup_connection(&fd_in, &fd_out, "git-ssh-pull", url, arg, argv + 1)) + return 1; + + service(fd_in, fd_out); diff --git a/t/t4100/t-apply-2.expect b/t/t4100/t-apply-2.expect new file mode 100644 index 0000000..d1e6459 --- /dev/null +++ b/t/t4100/t-apply-2.expect @@ -0,0 +1,5 @@ + Makefile | 2 +- + git-fetch-script | 5 ----- + git-pull-script | 34 +--------------------------------- + 3 files changed, 2 insertions(+), 39 deletions(-) + copy git-pull-script => git-fetch-script (87%) diff --git a/t/t4100/t-apply-2.patch b/t/t4100/t-apply-2.patch new file mode 100644 index 0000000..cfdc808 --- /dev/null +++ b/t/t4100/t-apply-2.patch @@ -0,0 +1,72 @@ +7ef76925d9c19ef74874e1735e2436e56d0c4897 (from 6b14d7faf0bad026a81a27bac07b47691f621b8f) +diff --git a/Makefile b/Makefile +--- a/Makefile ++++ b/Makefile +@@ -20,7 +20,7 @@ INSTALL=install + + SCRIPTS=git-apply-patch-script git-merge-one-file-script git-prune-script \ + git-pull-script git-tag-script git-resolve-script git-whatchanged \ +- git-deltafy-script ++ git-deltafy-script git-fetch-script + + PROG= git-update-cache git-diff-files git-init-db git-write-tree \ + git-read-tree git-commit-tree git-cat-file git-fsck-cache \ +diff --git a/git-pull-script b/git-fetch-script +similarity index 87% +copy from git-pull-script +copy to git-fetch-script +--- a/git-pull-script ++++ b/git-fetch-script +@@ -39,8 +39,3 @@ download_one "$merge_repo/$merge_name" " + + echo "Getting object database" + download_objects "$merge_repo" "$(cat "$GIT_DIR"/MERGE_HEAD)" +- +-git-resolve-script \ +- "$(cat "$GIT_DIR"/HEAD)" \ +- "$(cat "$GIT_DIR"/MERGE_HEAD)" \ +- "$merge_repo" +diff --git a/git-pull-script b/git-pull-script +--- a/git-pull-script ++++ b/git-pull-script +@@ -6,39 +6,7 @@ merge_name=${2:-HEAD} + : ${GIT_DIR=.git} + : ${GIT_OBJECT_DIRECTORY="${SHA1_FILE_DIRECTORY-"$GIT_DIR/objects"}"} + +-download_one () { +- # remote_path="$1" local_file="$2" +- case "$1" in +- http://*) +- wget -q -O "$2" "$1" ;; +- /*) +- test -f "$1" && cat >"$2" "$1" ;; +- *) +- rsync -L "$1" "$2" ;; +- esac +-} +- +-download_objects () { +- # remote_repo="$1" head_sha1="$2" +- case "$1" in +- http://*) +- git-http-pull -a "$2" "$1/" +- ;; +- /*) +- git-local-pull -l -a "$2" "$1/" +- ;; +- *) +- rsync -avz --ignore-existing \ +- "$1/objects/." "$GIT_OBJECT_DIRECTORY"/. +- ;; +- esac +-} +- +-echo "Getting remote $merge_name" +-download_one "$merge_repo/$merge_name" "$GIT_DIR"/MERGE_HEAD +- +-echo "Getting object database" +-download_objects "$merge_repo" "$(cat "$GIT_DIR"/MERGE_HEAD)" ++git-fetch-script "$merge_repo" "$merge_name" + + git-resolve-script \ + "$(cat "$GIT_DIR"/HEAD)" \ diff --git a/t/t4100/t-apply-3.expect b/t/t4100/t-apply-3.expect new file mode 100644 index 0000000..912a552 --- /dev/null +++ b/t/t4100/t-apply-3.expect @@ -0,0 +1,7 @@ + Documentation/git-ls-tree.txt | 20 +- + ls-tree.c | 459 ++++++++++++++++++++++------------------- + t/t3100-ls-tree-restrict.sh | 3 + tree.c | 2 + tree.h | 1 + 5 files changed, 262 insertions(+), 223 deletions(-) + rewrite ls-tree.c (82%) diff --git a/t/t4100/t-apply-3.patch b/t/t4100/t-apply-3.patch new file mode 100644 index 0000000..90cdbaa --- /dev/null +++ b/t/t4100/t-apply-3.patch @@ -0,0 +1,567 @@ +6af1f0192ff8740fe77db7cf02c739ccfbdf119c (from 2bc2564145835996734d6ed5d1880f85b17233d6) +diff --git a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt +--- a/Documentation/git-ls-tree.txt ++++ b/Documentation/git-ls-tree.txt +@@ -4,23 +4,26 @@ v0.1, May 2005 + + NAME + ---- +-git-ls-tree - Displays a tree object in human readable form ++git-ls-tree - Lists the contents of a tree object. + + + SYNOPSIS + -------- +-'git-ls-tree' [-r] [-z] [paths...] ++'git-ls-tree' [-d] [-r] [-z] [paths...] + + DESCRIPTION + ----------- +-Converts the tree object to a human readable (and script processable) +-form. ++Lists the contents of a tree object, like what "/bin/ls -a" does ++in the current working directory. + + OPTIONS + ------- + :: + Id of a tree. + ++-d:: ++ show only the named tree entry itself, not its children ++ + -r:: + recurse into sub-trees + +@@ -28,18 +31,19 @@ OPTIONS + \0 line termination on output + + paths:: +- Optionally, restrict the output of git-ls-tree to specific +- paths. Directories will only list their tree blob ids. +- Implies -r. ++ When paths are given, shows them. Otherwise implicitly ++ uses the root level of the tree as the sole path argument. ++ + + Output Format + ------------- +- \t \t \t ++ SP SP TAB + + + Author + ------ + Written by Linus Torvalds ++Completely rewritten from scratch by Junio C Hamano + + Documentation + -------------- +diff --git a/ls-tree.c b/ls-tree.c +dissimilarity index 82% +--- ls-tree.c ++++ ls-tree.c +@@ -1,212 +1,247 @@ +-/* +- * GIT - The information manager from hell +- * +- * Copyright (C) Linus Torvalds, 2005 +- */ +-#include "cache.h" +- +-static int line_termination = '\n'; +-static int recursive = 0; +- +-struct path_prefix { +- struct path_prefix *prev; +- const char *name; +-}; +- +-#define DEBUG(fmt, ...) +- +-static int string_path_prefix(char *buff, size_t blen, struct path_prefix *prefix) +-{ +- int len = 0; +- if (prefix) { +- if (prefix->prev) { +- len = string_path_prefix(buff,blen,prefix->prev); +- buff += len; +- blen -= len; +- if (blen > 0) { +- *buff = '/'; +- len++; +- buff++; +- blen--; +- } +- } +- strncpy(buff,prefix->name,blen); +- return len + strlen(prefix->name); +- } +- +- return 0; +-} +- +-static void print_path_prefix(struct path_prefix *prefix) +-{ +- if (prefix) { +- if (prefix->prev) { +- print_path_prefix(prefix->prev); +- putchar('/'); +- } +- fputs(prefix->name, stdout); +- } +-} +- +-/* +- * return: +- * -1 if prefix is *not* a subset of path +- * 0 if prefix == path +- * 1 if prefix is a subset of path +- */ +-static int pathcmp(const char *path, struct path_prefix *prefix) +-{ +- char buff[PATH_MAX]; +- int len,slen; +- +- if (prefix == NULL) +- return 1; +- +- len = string_path_prefix(buff, sizeof buff, prefix); +- slen = strlen(path); +- +- if (slen < len) +- return -1; +- +- if (strncmp(path,buff,len) == 0) { +- if (slen == len) +- return 0; +- else +- return 1; +- } +- +- return -1; +-} +- +-/* +- * match may be NULL, or a *sorted* list of paths +- */ +-static void list_recursive(void *buffer, +- const char *type, +- unsigned long size, +- struct path_prefix *prefix, +- char **match, int matches) +-{ +- struct path_prefix this_prefix; +- this_prefix.prev = prefix; +- +- if (strcmp(type, "tree")) +- die("expected a 'tree' node"); +- +- if (matches) +- recursive = 1; +- +- while (size) { +- int namelen = strlen(buffer)+1; +- void *eltbuf = NULL; +- char elttype[20]; +- unsigned long eltsize; +- unsigned char *sha1 = buffer + namelen; +- char *path = strchr(buffer, ' ') + 1; +- unsigned int mode; +- const char *matched = NULL; +- int mtype = -1; +- int mindex; +- +- if (size < namelen + 20 || sscanf(buffer, "%o", &mode) != 1) +- die("corrupt 'tree' file"); +- buffer = sha1 + 20; +- size -= namelen + 20; +- +- this_prefix.name = path; +- for ( mindex = 0; mindex < matches; mindex++) { +- mtype = pathcmp(match[mindex],&this_prefix); +- if (mtype >= 0) { +- matched = match[mindex]; +- break; +- } +- } +- +- /* +- * If we're not matching, or if this is an exact match, +- * print out the info +- */ +- if (!matches || (matched != NULL && mtype == 0)) { +- printf("%06o %s %s\t", mode, +- S_ISDIR(mode) ? "tree" : "blob", +- sha1_to_hex(sha1)); +- print_path_prefix(&this_prefix); +- putchar(line_termination); +- } +- +- if (! recursive || ! S_ISDIR(mode)) +- continue; +- +- if (matches && ! matched) +- continue; +- +- if (! (eltbuf = read_sha1_file(sha1, elttype, &eltsize)) ) { +- error("cannot read %s", sha1_to_hex(sha1)); +- continue; +- } +- +- /* If this is an exact directory match, we may have +- * directory files following this path. Match on them. +- * Otherwise, we're at a pach subcomponent, and we need +- * to try to match again. +- */ +- if (mtype == 0) +- mindex++; +- +- list_recursive(eltbuf, elttype, eltsize, &this_prefix, &match[mindex], matches-mindex); +- free(eltbuf); +- } +-} +- +-static int qcmp(const void *a, const void *b) +-{ +- return strcmp(*(char **)a, *(char **)b); +-} +- +-static int list(unsigned char *sha1,char **path) +-{ +- void *buffer; +- unsigned long size; +- int npaths; +- +- for (npaths = 0; path[npaths] != NULL; npaths++) +- ; +- +- qsort(path,npaths,sizeof(char *),qcmp); +- +- buffer = read_object_with_reference(sha1, "tree", &size, NULL); +- if (!buffer) +- die("unable to read sha1 file"); +- list_recursive(buffer, "tree", size, NULL, path, npaths); +- free(buffer); +- return 0; +-} +- +-static const char *ls_tree_usage = "git-ls-tree [-r] [-z] [paths...]"; +- +-int main(int argc, char **argv) +-{ +- unsigned char sha1[20]; +- +- while (1 < argc && argv[1][0] == '-') { +- switch (argv[1][1]) { +- case 'z': +- line_termination = 0; +- break; +- case 'r': +- recursive = 1; +- break; +- default: +- usage(ls_tree_usage); +- } +- argc--; argv++; +- } +- +- if (argc < 2) +- usage(ls_tree_usage); +- if (get_sha1(argv[1], sha1) < 0) +- usage(ls_tree_usage); +- if (list(sha1, &argv[2]) < 0) +- die("list failed"); +- return 0; +-} ++/* ++ * GIT - The information manager from hell ++ * ++ * Copyright (C) Linus Torvalds, 2005 ++ */ ++#include "cache.h" ++#include "blob.h" ++#include "tree.h" ++ ++static int line_termination = '\n'; ++#define LS_RECURSIVE 1 ++#define LS_TREE_ONLY 2 ++static int ls_options = 0; ++ ++static struct tree_entry_list root_entry; ++ ++static void prepare_root(unsigned char *sha1) ++{ ++ unsigned char rsha[20]; ++ unsigned long size; ++ void *buf; ++ struct tree *root_tree; ++ ++ buf = read_object_with_reference(sha1, "tree", &size, rsha); ++ free(buf); ++ if (!buf) ++ die("Could not read %s", sha1_to_hex(sha1)); ++ ++ root_tree = lookup_tree(rsha); ++ if (!root_tree) ++ die("Could not read %s", sha1_to_hex(sha1)); ++ ++ /* Prepare a fake entry */ ++ root_entry.directory = 1; ++ root_entry.executable = root_entry.symlink = 0; ++ root_entry.mode = S_IFDIR; ++ root_entry.name = ""; ++ root_entry.item.tree = root_tree; ++ root_entry.parent = NULL; ++} ++ ++static int prepare_children(struct tree_entry_list *elem) ++{ ++ if (!elem->directory) ++ return -1; ++ if (!elem->item.tree->object.parsed) { ++ struct tree_entry_list *e; ++ if (parse_tree(elem->item.tree)) ++ return -1; ++ /* Set up the parent link */ ++ for (e = elem->item.tree->entries; e; e = e->next) ++ e->parent = elem; ++ } ++ return 0; ++} ++ ++static struct tree_entry_list *find_entry_0(struct tree_entry_list *elem, ++ const char *path, ++ const char *path_end) ++{ ++ const char *ep; ++ int len; ++ ++ while (path < path_end) { ++ if (prepare_children(elem)) ++ return NULL; ++ ++ /* In elem->tree->entries, find the one that has name ++ * that matches what is between path and ep. ++ */ ++ elem = elem->item.tree->entries; ++ ++ ep = strchr(path, '/'); ++ if (!ep || path_end <= ep) ++ ep = path_end; ++ len = ep - path; ++ ++ while (elem) { ++ if ((strlen(elem->name) == len) && ++ !strncmp(elem->name, path, len)) ++ break; ++ elem = elem->next; ++ } ++ if (path_end <= ep || !elem) ++ return elem; ++ while (*ep == '/' && ep < path_end) ++ ep++; ++ path = ep; ++ } ++ return NULL; ++} ++ ++static struct tree_entry_list *find_entry(const char *path, ++ const char *path_end) ++{ ++ /* Find tree element, descending from root, that ++ * corresponds to the named path, lazily expanding ++ * the tree if possible. ++ */ ++ if (path == path_end) { ++ /* Special. This is the root level */ ++ return &root_entry; ++ } ++ return find_entry_0(&root_entry, path, path_end); ++} ++ ++static void show_entry_name(struct tree_entry_list *e) ++{ ++ /* This is yucky. The root level is there for ++ * our convenience but we really want to do a ++ * forest. ++ */ ++ if (e->parent && e->parent != &root_entry) { ++ show_entry_name(e->parent); ++ putchar('/'); ++ } ++ printf("%s", e->name); ++} ++ ++static const char *entry_type(struct tree_entry_list *e) ++{ ++ return (e->directory ? "tree" : "blob"); ++} ++ ++static const char *entry_hex(struct tree_entry_list *e) ++{ ++ return sha1_to_hex(e->directory ++ ? e->item.tree->object.sha1 ++ : e->item.blob->object.sha1); ++} ++ ++/* forward declaration for mutually recursive routines */ ++static int show_entry(struct tree_entry_list *, int); ++ ++static int show_children(struct tree_entry_list *e, int level) ++{ ++ if (prepare_children(e)) ++ die("internal error: ls-tree show_children called with non tree"); ++ e = e->item.tree->entries; ++ while (e) { ++ show_entry(e, level); ++ e = e->next; ++ } ++ return 0; ++} ++ ++static int show_entry(struct tree_entry_list *e, int level) ++{ ++ int err = 0; ++ ++ if (e != &root_entry) { ++ printf("%06o %s %s ", e->mode, entry_type(e), ++ entry_hex(e)); ++ show_entry_name(e); ++ putchar(line_termination); ++ } ++ ++ if (e->directory) { ++ /* If this is a directory, we have the following cases: ++ * (1) This is the top-level request (explicit path from the ++ * command line, or "root" if there is no command line). ++ * a. Without any flag. We show direct children. We do not ++ * recurse into them. ++ * b. With -r. We do recurse into children. ++ * c. With -d. We do not recurse into children. ++ * (2) We came here because our caller is either (1-a) or ++ * (1-b). ++ * a. Without any flag. We do not show our children (which ++ * are grandchildren for the original request). ++ * b. With -r. We continue to recurse into our children. ++ * c. With -d. We should not have come here to begin with. ++ */ ++ if (level == 0 && !(ls_options & LS_TREE_ONLY)) ++ /* case (1)-a and (1)-b */ ++ err = err | show_children(e, level+1); ++ else if (level && ls_options & LS_RECURSIVE) ++ /* case (2)-b */ ++ err = err | show_children(e, level+1); ++ } ++ return err; ++} ++ ++static int list_one(const char *path, const char *path_end) ++{ ++ int err = 0; ++ struct tree_entry_list *e = find_entry(path, path_end); ++ if (!e) { ++ /* traditionally ls-tree does not complain about ++ * missing path. We may change this later to match ++ * what "/bin/ls -a" does, which is to complain. ++ */ ++ return err; ++ } ++ err = err | show_entry(e, 0); ++ return err; ++} ++ ++static int list(char **path) ++{ ++ int i; ++ int err = 0; ++ for (i = 0; path[i]; i++) { ++ int len = strlen(path[i]); ++ while (0 <= len && path[i][len] == '/') ++ len--; ++ err = err | list_one(path[i], path[i] + len); ++ } ++ return err; ++} ++ ++static const char *ls_tree_usage = ++ "git-ls-tree [-d] [-r] [-z] [path...]"; ++ ++int main(int argc, char **argv) ++{ ++ static char *path0[] = { "", NULL }; ++ char **path; ++ unsigned char sha1[20]; ++ ++ while (1 < argc && argv[1][0] == '-') { ++ switch (argv[1][1]) { ++ case 'z': ++ line_termination = 0; ++ break; ++ case 'r': ++ ls_options |= LS_RECURSIVE; ++ break; ++ case 'd': ++ ls_options |= LS_TREE_ONLY; ++ break; ++ default: ++ usage(ls_tree_usage); ++ } ++ argc--; argv++; ++ } ++ ++ if (argc < 2) ++ usage(ls_tree_usage); ++ if (get_sha1(argv[1], sha1) < 0) ++ usage(ls_tree_usage); ++ ++ path = (argc == 2) ? path0 : (argv + 2); ++ prepare_root(sha1); ++ if (list(path) < 0) ++ die("list failed"); ++ return 0; ++} +diff --git a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh +--- a/t/t3100-ls-tree-restrict.sh ++++ b/t/t3100-ls-tree-restrict.sh +@@ -74,8 +74,8 @@ test_expect_success \ + 'ls-tree filtered' \ + 'git-ls-tree $tree path1 path0 >current && + cat >expected <<\EOF && +-100644 blob X path0 + 120000 blob X path1 ++100644 blob X path0 + EOF + test_output' + +@@ -85,7 +85,6 @@ test_expect_success \ + cat >expected <<\EOF && + 040000 tree X path2 + 040000 tree X path2/baz +-100644 blob X path2/baz/b + 120000 blob X path2/bazbo + 100644 blob X path2/foo + EOF +diff --git a/tree.c b/tree.c +--- a/tree.c ++++ b/tree.c +@@ -133,7 +133,7 @@ int parse_tree_buffer(struct tree *item, + } + if (obj) + add_ref(&item->object, obj); +- ++ entry->parent = NULL; /* needs to be filled by the user */ + *list_p = entry; + list_p = &entry->next; + } +diff --git a/tree.h b/tree.h +--- a/tree.h ++++ b/tree.h +@@ -16,6 +16,7 @@ struct tree_entry_list { + struct tree *tree; + struct blob *blob; + } item; ++ struct tree_entry_list *parent; + }; + + struct tree { diff --git a/t/t4100/t-apply-4.expect b/t/t4100/t-apply-4.expect new file mode 100644 index 0000000..1ec028b --- /dev/null +++ b/t/t4100/t-apply-4.expect @@ -0,0 +1,5 @@ + t/t0000-basic.sh | 0 + t/test-lib.sh | 0 + 2 files changed, 0 insertions(+), 0 deletions(-) + mode change 100644 => 100755 t/t0000-basic.sh + mode change 100644 => 100755 t/test-lib.sh diff --git a/t/t4100/t-apply-4.patch b/t/t4100/t-apply-4.patch new file mode 100644 index 0000000..4a56ab5 --- /dev/null +++ b/t/t4100/t-apply-4.patch @@ -0,0 +1,7 @@ +ceede59ea90cebad52ba9c8263fef3fb6ef17593 (from 368f99d57e8ed17243f2e164431449d48bfca2fb) +diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh +old mode 100644 +new mode 100755 +diff --git a/t/test-lib.sh b/t/test-lib.sh +old mode 100644 +new mode 100755 diff --git a/t/t4100/t-apply-5.expect b/t/t4100/t-apply-5.expect new file mode 100644 index 0000000..b387df1 --- /dev/null +++ b/t/t4100/t-apply-5.expect @@ -0,0 +1,19 @@ + Documentation/git-rpull.txt | 50 ------------------- + Documentation/git-rpush.txt | 30 ------------ + Documentation/git-ssh-pull.txt | 50 +++++++++++++++++++ + Documentation/git-ssh-push.txt | 30 ++++++++++++ + Documentation/git.txt | 6 +- + Makefile | 6 +- + rpull.c | 83 -------------------------------- + rpush.c | 104 ---------------------------------------- + ssh-pull.c | 83 ++++++++++++++++++++++++++++++++ + ssh-push.c | 104 ++++++++++++++++++++++++++++++++++++++++ + 10 files changed, 273 insertions(+), 273 deletions(-) + delete Documentation/git-rpull.txt + delete Documentation/git-rpush.txt + create Documentation/git-ssh-pull.txt + create Documentation/git-ssh-push.txt + delete rpull.c + delete rpush.c + create ssh-pull.c + create ssh-push.c diff --git a/t/t4100/t-apply-5.patch b/t/t4100/t-apply-5.patch new file mode 100644 index 0000000..de11623 --- /dev/null +++ b/t/t4100/t-apply-5.patch @@ -0,0 +1,612 @@ +diff a/Documentation/git-rpull.txt b/Documentation/git-rpull.txt +--- a/Documentation/git-rpull.txt ++++ /dev/null +@@ -1,50 +0,0 @@ +-git-rpull(1) +-============ +-v0.1, May 2005 +- +-NAME +----- +-git-rpull - Pulls from a remote repository over ssh connection +- +- +- +-SYNOPSIS +--------- +-'git-rpull' [-c] [-t] [-a] [-d] [-v] [--recover] commit-id url +- +-DESCRIPTION +------------ +-Pulls from a remote repository over ssh connection, invoking git-rpush on +-the other end. +- +-OPTIONS +-------- +--c:: +- Get the commit objects. +--t:: +- Get trees associated with the commit objects. +--a:: +- Get all the objects. +--d:: +- Do not check for delta base objects (use this option +- only when you know the remote repository is not +- deltified). +---recover:: +- Check dependency of deltified object more carefully than +- usual, to recover after earlier pull that was interrupted. +--v:: +- Report what is downloaded. +- +- +-Author +------- +-Written by Linus Torvalds +- +-Documentation +--------------- +-Documentation by David Greaves, Junio C Hamano and the git-list . +- +-GIT +---- +-Part of the link:git.html[git] suite +- +diff a/Documentation/git-rpush.txt b/Documentation/git-rpush.txt +--- a/Documentation/git-rpush.txt ++++ /dev/null +@@ -1,30 +0,0 @@ +-git-rpush(1) +-============ +-v0.1, May 2005 +- +-NAME +----- +-git-rpush - Helper "server-side" program used by git-rpull +- +- +-SYNOPSIS +--------- +-'git-rpush' +- +-DESCRIPTION +------------ +-Helper "server-side" program used by git-rpull. +- +- +-Author +------- +-Written by Linus Torvalds +- +-Documentation +--------------- +-Documentation by David Greaves, Junio C Hamano and the git-list . +- +-GIT +---- +-Part of the link:git.html[git] suite +- +diff a/Documentation/git-ssh-pull.txt b/Documentation/git-ssh-pull.txt +--- /dev/null ++++ b/Documentation/git-ssh-pull.txt +@@ -0,0 +1,50 @@ ++git-ssh-pull(1) ++=============== ++v0.1, May 2005 ++ ++NAME ++---- ++git-ssh-pull - Pulls from a remote repository over ssh connection ++ ++ ++ ++SYNOPSIS ++-------- ++'git-ssh-pull' [-c] [-t] [-a] [-d] [-v] [--recover] commit-id url ++ ++DESCRIPTION ++----------- ++Pulls from a remote repository over ssh connection, invoking git-ssh-push ++on the other end. ++ ++OPTIONS ++------- ++-c:: ++ Get the commit objects. ++-t:: ++ Get trees associated with the commit objects. ++-a:: ++ Get all the objects. ++-d:: ++ Do not check for delta base objects (use this option ++ only when you know the remote repository is not ++ deltified). ++--recover:: ++ Check dependency of deltified object more carefully than ++ usual, to recover after earlier pull that was interrupted. ++-v:: ++ Report what is downloaded. ++ ++ ++Author ++------ ++Written by Linus Torvalds ++ ++Documentation ++-------------- ++Documentation by David Greaves, Junio C Hamano and the git-list . ++ ++GIT ++--- ++Part of the link:git.html[git] suite ++ +diff a/Documentation/git-ssh-push.txt b/Documentation/git-ssh-push.txt +--- /dev/null ++++ b/Documentation/git-ssh-push.txt +@@ -0,0 +1,30 @@ ++git-ssh-push(1) ++=============== ++v0.1, May 2005 ++ ++NAME ++---- ++git-ssh-push - Helper "server-side" program used by git-ssh-pull ++ ++ ++SYNOPSIS ++-------- ++'git-ssh-push' ++ ++DESCRIPTION ++----------- ++Helper "server-side" program used by git-ssh-pull. ++ ++ ++Author ++------ ++Written by Linus Torvalds ++ ++Documentation ++-------------- ++Documentation by David Greaves, Junio C Hamano and the git-list . ++ ++GIT ++--- ++Part of the link:git.html[git] suite ++ +diff a/Documentation/git.txt b/Documentation/git.txt +--- a/Documentation/git.txt ++++ b/Documentation/git.txt +@@ -148,7 +148,7 @@ link:git-resolve-script.html[git-resolve + link:git-tag-script.html[git-tag-script]:: + An example script to create a tag object signed with GPG + +-link:git-rpull.html[git-rpull]:: ++link:git-ssh-pull.html[git-ssh-pull]:: + Pulls from a remote repository over ssh connection + + Interogators: +@@ -156,8 +156,8 @@ Interogators: + link:git-diff-helper.html[git-diff-helper]:: + Generates patch format output for git-diff-* + +-link:git-rpush.html[git-rpush]:: +- Helper "server-side" program used by git-rpull ++link:git-ssh-push.html[git-ssh-push]:: ++ Helper "server-side" program used by git-ssh-pull + + + +diff a/Makefile b/Makefile +--- a/Makefile ++++ b/Makefile +@@ -30,7 +30,7 @@ PROG= git-update-cache git-diff-files + git-checkout-cache git-diff-tree git-rev-tree git-ls-files \ + git-check-files git-ls-tree git-merge-base git-merge-cache \ + git-unpack-file git-export git-diff-cache git-convert-cache \ +- git-http-pull git-rpush git-rpull git-rev-list git-mktag \ ++ git-http-pull git-ssh-push git-ssh-pull git-rev-list git-mktag \ + git-diff-helper git-tar-tree git-local-pull git-write-blob \ + git-get-tar-commit-id git-mkdelta git-apply git-stripspace + +@@ -105,8 +105,8 @@ git-diff-cache: diff-cache.c + git-convert-cache: convert-cache.c + git-http-pull: http-pull.c pull.c + git-local-pull: local-pull.c pull.c +-git-rpush: rsh.c +-git-rpull: rsh.c pull.c ++git-ssh-push: rsh.c ++git-ssh-pull: rsh.c pull.c + git-rev-list: rev-list.c + git-mktag: mktag.c + git-diff-helper: diff-helper.c +diff a/rpull.c b/rpull.c +--- a/rpull.c ++++ /dev/null +@@ -1,83 +0,0 @@ +-#include "cache.h" +-#include "commit.h" +-#include "rsh.h" +-#include "pull.h" +- +-static int fd_in; +-static int fd_out; +- +-static unsigned char remote_version = 0; +-static unsigned char local_version = 1; +- +-int fetch(unsigned char *sha1) +-{ +- int ret; +- signed char remote; +- char type = 'o'; +- if (has_sha1_file(sha1)) +- return 0; +- write(fd_out, &type, 1); +- write(fd_out, sha1, 20); +- if (read(fd_in, &remote, 1) < 1) +- return -1; +- if (remote < 0) +- return remote; +- ret = write_sha1_from_fd(sha1, fd_in); +- if (!ret) +- pull_say("got %s\n", sha1_to_hex(sha1)); +- return ret; +-} +- +-int get_version(void) +-{ +- char type = 'v'; +- write(fd_out, &type, 1); +- write(fd_out, &local_version, 1); +- if (read(fd_in, &remote_version, 1) < 1) { +- return error("Couldn't read version from remote end"); +- } +- return 0; +-} +- +-int main(int argc, char **argv) +-{ +- char *commit_id; +- char *url; +- int arg = 1; +- +- while (arg < argc && argv[arg][0] == '-') { +- if (argv[arg][1] == 't') { +- get_tree = 1; +- } else if (argv[arg][1] == 'c') { +- get_history = 1; +- } else if (argv[arg][1] == 'd') { +- get_delta = 0; +- } else if (!strcmp(argv[arg], "--recover")) { +- get_delta = 2; +- } else if (argv[arg][1] == 'a') { +- get_all = 1; +- get_tree = 1; +- get_history = 1; +- } else if (argv[arg][1] == 'v') { +- get_verbosely = 1; +- } +- arg++; +- } +- if (argc < arg + 2) { +- usage("git-rpull [-c] [-t] [-a] [-v] [-d] [--recover] commit-id url"); +- return 1; +- } +- commit_id = argv[arg]; +- url = argv[arg + 1]; +- +- if (setup_connection(&fd_in, &fd_out, "git-rpush", url, arg, argv + 1)) +- return 1; +- +- if (get_version()) +- return 1; +- +- if (pull(commit_id)) +- return 1; +- +- return 0; +-} +diff a/rpush.c b/rpush.c +--- a/rpush.c ++++ /dev/null +@@ -1,104 +0,0 @@ +-#include "cache.h" +-#include "rsh.h" +-#include +-#include +- +-unsigned char local_version = 1; +-unsigned char remote_version = 0; +- +-int serve_object(int fd_in, int fd_out) { +- ssize_t size; +- int posn = 0; +- char sha1[20]; +- unsigned long objsize; +- void *buf; +- signed char remote; +- do { +- size = read(fd_in, sha1 + posn, 20 - posn); +- if (size < 0) { +- perror("git-rpush: read "); +- return -1; +- } +- if (!size) +- return -1; +- posn += size; +- } while (posn < 20); +- +- /* fprintf(stderr, "Serving %s\n", sha1_to_hex(sha1)); */ +- remote = 0; +- +- buf = map_sha1_file(sha1, &objsize); +- +- if (!buf) { +- fprintf(stderr, "git-rpush: could not find %s\n", +- sha1_to_hex(sha1)); +- remote = -1; +- } +- +- write(fd_out, &remote, 1); +- +- if (remote < 0) +- return 0; +- +- posn = 0; +- do { +- size = write(fd_out, buf + posn, objsize - posn); +- if (size <= 0) { +- if (!size) { +- fprintf(stderr, "git-rpush: write closed"); +- } else { +- perror("git-rpush: write "); +- } +- return -1; +- } +- posn += size; +- } while (posn < objsize); +- return 0; +-} +- +-int serve_version(int fd_in, int fd_out) +-{ +- if (read(fd_in, &remote_version, 1) < 1) +- return -1; +- write(fd_out, &local_version, 1); +- return 0; +-} +- +-void service(int fd_in, int fd_out) { +- char type; +- int retval; +- do { +- retval = read(fd_in, &type, 1); +- if (retval < 1) { +- if (retval < 0) +- perror("rpush: read "); +- return; +- } +- if (type == 'v' && serve_version(fd_in, fd_out)) +- return; +- if (type == 'o' && serve_object(fd_in, fd_out)) +- return; +- } while (1); +-} +- +-int main(int argc, char **argv) +-{ +- int arg = 1; +- char *commit_id; +- char *url; +- int fd_in, fd_out; +- while (arg < argc && argv[arg][0] == '-') { +- arg++; +- } +- if (argc < arg + 2) { +- usage("git-rpush [-c] [-t] [-a] commit-id url"); +- return 1; +- } +- commit_id = argv[arg]; +- url = argv[arg + 1]; +- if (setup_connection(&fd_in, &fd_out, "git-rpull", url, arg, argv + 1)) +- return 1; +- +- service(fd_in, fd_out); +- return 0; +-} +diff a/ssh-pull.c b/ssh-pull.c +--- /dev/null ++++ b/ssh-pull.c +@@ -0,0 +1,83 @@ ++#include "cache.h" ++#include "commit.h" ++#include "rsh.h" ++#include "pull.h" ++ ++static int fd_in; ++static int fd_out; ++ ++static unsigned char remote_version = 0; ++static unsigned char local_version = 1; ++ ++int fetch(unsigned char *sha1) ++{ ++ int ret; ++ signed char remote; ++ char type = 'o'; ++ if (has_sha1_file(sha1)) ++ return 0; ++ write(fd_out, &type, 1); ++ write(fd_out, sha1, 20); ++ if (read(fd_in, &remote, 1) < 1) ++ return -1; ++ if (remote < 0) ++ return remote; ++ ret = write_sha1_from_fd(sha1, fd_in); ++ if (!ret) ++ pull_say("got %s\n", sha1_to_hex(sha1)); ++ return ret; ++} ++ ++int get_version(void) ++{ ++ char type = 'v'; ++ write(fd_out, &type, 1); ++ write(fd_out, &local_version, 1); ++ if (read(fd_in, &remote_version, 1) < 1) { ++ return error("Couldn't read version from remote end"); ++ } ++ return 0; ++} ++ ++int main(int argc, char **argv) ++{ ++ char *commit_id; ++ char *url; ++ int arg = 1; ++ ++ while (arg < argc && argv[arg][0] == '-') { ++ if (argv[arg][1] == 't') { ++ get_tree = 1; ++ } else if (argv[arg][1] == 'c') { ++ get_history = 1; ++ } else if (argv[arg][1] == 'd') { ++ get_delta = 0; ++ } else if (!strcmp(argv[arg], "--recover")) { ++ get_delta = 2; ++ } else if (argv[arg][1] == 'a') { ++ get_all = 1; ++ get_tree = 1; ++ get_history = 1; ++ } else if (argv[arg][1] == 'v') { ++ get_verbosely = 1; ++ } ++ arg++; ++ } ++ if (argc < arg + 2) { ++ usage("git-ssh-pull [-c] [-t] [-a] [-v] [-d] [--recover] commit-id url"); ++ return 1; ++ } ++ commit_id = argv[arg]; ++ url = argv[arg + 1]; ++ ++ if (setup_connection(&fd_in, &fd_out, "git-ssh-push", url, arg, argv + 1)) ++ return 1; ++ ++ if (get_version()) ++ return 1; ++ ++ if (pull(commit_id)) ++ return 1; ++ ++ return 0; ++} +diff a/ssh-push.c b/ssh-push.c +--- /dev/null ++++ b/ssh-push.c +@@ -0,0 +1,104 @@ ++#include "cache.h" ++#include "rsh.h" ++#include ++#include ++ ++unsigned char local_version = 1; ++unsigned char remote_version = 0; ++ ++int serve_object(int fd_in, int fd_out) { ++ ssize_t size; ++ int posn = 0; ++ char sha1[20]; ++ unsigned long objsize; ++ void *buf; ++ signed char remote; ++ do { ++ size = read(fd_in, sha1 + posn, 20 - posn); ++ if (size < 0) { ++ perror("git-ssh-push: read "); ++ return -1; ++ } ++ if (!size) ++ return -1; ++ posn += size; ++ } while (posn < 20); ++ ++ /* fprintf(stderr, "Serving %s\n", sha1_to_hex(sha1)); */ ++ remote = 0; ++ ++ buf = map_sha1_file(sha1, &objsize); ++ ++ if (!buf) { ++ fprintf(stderr, "git-ssh-push: could not find %s\n", ++ sha1_to_hex(sha1)); ++ remote = -1; ++ } ++ ++ write(fd_out, &remote, 1); ++ ++ if (remote < 0) ++ return 0; ++ ++ posn = 0; ++ do { ++ size = write(fd_out, buf + posn, objsize - posn); ++ if (size <= 0) { ++ if (!size) { ++ fprintf(stderr, "git-ssh-push: write closed"); ++ } else { ++ perror("git-ssh-push: write "); ++ } ++ return -1; ++ } ++ posn += size; ++ } while (posn < objsize); ++ return 0; ++} ++ ++int serve_version(int fd_in, int fd_out) ++{ ++ if (read(fd_in, &remote_version, 1) < 1) ++ return -1; ++ write(fd_out, &local_version, 1); ++ return 0; ++} ++ ++void service(int fd_in, int fd_out) { ++ char type; ++ int retval; ++ do { ++ retval = read(fd_in, &type, 1); ++ if (retval < 1) { ++ if (retval < 0) ++ perror("git-ssh-push: read "); ++ return; ++ } ++ if (type == 'v' && serve_version(fd_in, fd_out)) ++ return; ++ if (type == 'o' && serve_object(fd_in, fd_out)) ++ return; ++ } while (1); ++} ++ ++int main(int argc, char **argv) ++{ ++ int arg = 1; ++ char *commit_id; ++ char *url; ++ int fd_in, fd_out; ++ while (arg < argc && argv[arg][0] == '-') { ++ arg++; ++ } ++ if (argc < arg + 2) { ++ usage("git-ssh-push [-c] [-t] [-a] commit-id url"); ++ return 1; ++ } ++ commit_id = argv[arg]; ++ url = argv[arg + 1]; ++ if (setup_connection(&fd_in, &fd_out, "git-ssh-pull", url, arg, argv + 1)) ++ return 1; ++ ++ service(fd_in, fd_out); ++ return 0; ++} diff --git a/t/t4100/t-apply-6.expect b/t/t4100/t-apply-6.expect new file mode 100644 index 0000000..1c343d4 --- /dev/null +++ b/t/t4100/t-apply-6.expect @@ -0,0 +1,5 @@ + Makefile | 2 +- + git-fetch-script | 41 +++++++++++++++++++++++++++++++++++++++++ + git-pull-script | 34 +--------------------------------- + 3 files changed, 43 insertions(+), 34 deletions(-) + create git-fetch-script diff --git a/t/t4100/t-apply-6.patch b/t/t4100/t-apply-6.patch new file mode 100644 index 0000000..d975363 --- /dev/null +++ b/t/t4100/t-apply-6.patch @@ -0,0 +1,101 @@ +diff a/Makefile b/Makefile +--- a/Makefile ++++ b/Makefile +@@ -20,7 +20,7 @@ INSTALL=install + + SCRIPTS=git-apply-patch-script git-merge-one-file-script git-prune-script \ + git-pull-script git-tag-script git-resolve-script git-whatchanged \ +- git-deltafy-script ++ git-deltafy-script git-fetch-script + + PROG= git-update-cache git-diff-files git-init-db git-write-tree \ + git-read-tree git-commit-tree git-cat-file git-fsck-cache \ +diff a/git-fetch-script b/git-fetch-script +--- /dev/null ++++ b/git-fetch-script +@@ -0,0 +1,41 @@ ++#!/bin/sh ++# ++merge_repo=$1 ++merge_name=${2:-HEAD} ++ ++: ${GIT_DIR=.git} ++: ${GIT_OBJECT_DIRECTORY="${SHA1_FILE_DIRECTORY-"$GIT_DIR/objects"}"} ++ ++download_one () { ++ # remote_path="$1" local_file="$2" ++ case "$1" in ++ http://*) ++ wget -q -O "$2" "$1" ;; ++ /*) ++ test -f "$1" && cat >"$2" "$1" ;; ++ *) ++ rsync -L "$1" "$2" ;; ++ esac ++} ++ ++download_objects () { ++ # remote_repo="$1" head_sha1="$2" ++ case "$1" in ++ http://*) ++ git-http-pull -a "$2" "$1/" ++ ;; ++ /*) ++ git-local-pull -l -a "$2" "$1/" ++ ;; ++ *) ++ rsync -avz --ignore-existing \ ++ "$1/objects/." "$GIT_OBJECT_DIRECTORY"/. ++ ;; ++ esac ++} ++ ++echo "Getting remote $merge_name" ++download_one "$merge_repo/$merge_name" "$GIT_DIR"/MERGE_HEAD ++ ++echo "Getting object database" ++download_objects "$merge_repo" "$(cat "$GIT_DIR"/MERGE_HEAD)" +diff a/git-pull-script b/git-pull-script +--- a/git-pull-script ++++ b/git-pull-script +@@ -6,39 +6,7 @@ merge_name=${2:-HEAD} + : ${GIT_DIR=.git} + : ${GIT_OBJECT_DIRECTORY="${SHA1_FILE_DIRECTORY-"$GIT_DIR/objects"}"} + +-download_one () { +- # remote_path="$1" local_file="$2" +- case "$1" in +- http://*) +- wget -q -O "$2" "$1" ;; +- /*) +- test -f "$1" && cat >"$2" "$1" ;; +- *) +- rsync -L "$1" "$2" ;; +- esac +-} +- +-download_objects () { +- # remote_repo="$1" head_sha1="$2" +- case "$1" in +- http://*) +- git-http-pull -a "$2" "$1/" +- ;; +- /*) +- git-local-pull -l -a "$2" "$1/" +- ;; +- *) +- rsync -avz --ignore-existing \ +- "$1/objects/." "$GIT_OBJECT_DIRECTORY"/. +- ;; +- esac +-} +- +-echo "Getting remote $merge_name" +-download_one "$merge_repo/$merge_name" "$GIT_DIR"/MERGE_HEAD +- +-echo "Getting object database" +-download_objects "$merge_repo" "$(cat "$GIT_DIR"/MERGE_HEAD)" ++git-fetch-script "$merge_repo" "$merge_name" + + git-resolve-script \ + "$(cat "$GIT_DIR"/HEAD)" \ diff --git a/t/t4100/t-apply-7.expect b/t/t4100/t-apply-7.expect new file mode 100644 index 0000000..1283164 --- /dev/null +++ b/t/t4100/t-apply-7.expect @@ -0,0 +1,6 @@ + Documentation/git-ls-tree.txt | 20 +- + ls-tree.c | 333 +++++++++++++++++++++++------------------ + t/t3100-ls-tree-restrict.sh | 3 + tree.c | 2 + tree.h | 1 + 5 files changed, 199 insertions(+), 160 deletions(-) diff --git a/t/t4100/t-apply-7.patch b/t/t4100/t-apply-7.patch new file mode 100644 index 0000000..07c6589 --- /dev/null +++ b/t/t4100/t-apply-7.patch @@ -0,0 +1,494 @@ +diff a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt +--- a/Documentation/git-ls-tree.txt ++++ b/Documentation/git-ls-tree.txt +@@ -4,23 +4,26 @@ v0.1, May 2005 + + NAME + ---- +-git-ls-tree - Displays a tree object in human readable form ++git-ls-tree - Lists the contents of a tree object. + + + SYNOPSIS + -------- +-'git-ls-tree' [-r] [-z] [paths...] ++'git-ls-tree' [-d] [-r] [-z] [paths...] + + DESCRIPTION + ----------- +-Converts the tree object to a human readable (and script processable) +-form. ++Lists the contents of a tree object, like what "/bin/ls -a" does ++in the current working directory. + + OPTIONS + ------- + :: + Id of a tree. + ++-d:: ++ show only the named tree entry itself, not its children ++ + -r:: + recurse into sub-trees + +@@ -28,18 +31,19 @@ OPTIONS + \0 line termination on output + + paths:: +- Optionally, restrict the output of git-ls-tree to specific +- paths. Directories will only list their tree blob ids. +- Implies -r. ++ When paths are given, shows them. Otherwise implicitly ++ uses the root level of the tree as the sole path argument. ++ + + Output Format + ------------- +- \t \t \t ++ SP SP TAB + + + Author + ------ + Written by Linus Torvalds ++Completely rewritten from scratch by Junio C Hamano + + Documentation + -------------- +diff a/ls-tree.c b/ls-tree.c +--- a/ls-tree.c ++++ b/ls-tree.c +@@ -4,188 +4,217 @@ + * Copyright (C) Linus Torvalds, 2005 + */ + #include "cache.h" ++#include "blob.h" ++#include "tree.h" + + static int line_termination = '\n'; +-static int recursive = 0; ++#define LS_RECURSIVE 1 ++#define LS_TREE_ONLY 2 ++static int ls_options = 0; + +-struct path_prefix { +- struct path_prefix *prev; +- const char *name; +-}; +- +-#define DEBUG(fmt, ...) +- +-static int string_path_prefix(char *buff, size_t blen, struct path_prefix *prefix) +-{ +- int len = 0; +- if (prefix) { +- if (prefix->prev) { +- len = string_path_prefix(buff,blen,prefix->prev); +- buff += len; +- blen -= len; +- if (blen > 0) { +- *buff = '/'; +- len++; +- buff++; +- blen--; +- } +- } +- strncpy(buff,prefix->name,blen); +- return len + strlen(prefix->name); +- } ++static struct tree_entry_list root_entry; + +- return 0; ++static void prepare_root(unsigned char *sha1) ++{ ++ unsigned char rsha[20]; ++ unsigned long size; ++ void *buf; ++ struct tree *root_tree; ++ ++ buf = read_object_with_reference(sha1, "tree", &size, rsha); ++ free(buf); ++ if (!buf) ++ die("Could not read %s", sha1_to_hex(sha1)); ++ ++ root_tree = lookup_tree(rsha); ++ if (!root_tree) ++ die("Could not read %s", sha1_to_hex(sha1)); ++ ++ /* Prepare a fake entry */ ++ root_entry.directory = 1; ++ root_entry.executable = root_entry.symlink = 0; ++ root_entry.mode = S_IFDIR; ++ root_entry.name = ""; ++ root_entry.item.tree = root_tree; ++ root_entry.parent = NULL; + } + +-static void print_path_prefix(struct path_prefix *prefix) ++static int prepare_children(struct tree_entry_list *elem) + { +- if (prefix) { +- if (prefix->prev) { +- print_path_prefix(prefix->prev); +- putchar('/'); +- } +- fputs(prefix->name, stdout); ++ if (!elem->directory) ++ return -1; ++ if (!elem->item.tree->object.parsed) { ++ struct tree_entry_list *e; ++ if (parse_tree(elem->item.tree)) ++ return -1; ++ /* Set up the parent link */ ++ for (e = elem->item.tree->entries; e; e = e->next) ++ e->parent = elem; + } ++ return 0; + } + +-/* +- * return: +- * -1 if prefix is *not* a subset of path +- * 0 if prefix == path +- * 1 if prefix is a subset of path +- */ +-static int pathcmp(const char *path, struct path_prefix *prefix) +-{ +- char buff[PATH_MAX]; +- int len,slen; ++static struct tree_entry_list *find_entry_0(struct tree_entry_list *elem, ++ const char *path, ++ const char *path_end) ++{ ++ const char *ep; ++ int len; ++ ++ while (path < path_end) { ++ if (prepare_children(elem)) ++ return NULL; + +- if (prefix == NULL) +- return 1; ++ /* In elem->tree->entries, find the one that has name ++ * that matches what is between path and ep. ++ */ ++ elem = elem->item.tree->entries; + +- len = string_path_prefix(buff, sizeof buff, prefix); +- slen = strlen(path); ++ ep = strchr(path, '/'); ++ if (!ep || path_end <= ep) ++ ep = path_end; ++ len = ep - path; ++ ++ while (elem) { ++ if ((strlen(elem->name) == len) && ++ !strncmp(elem->name, path, len)) ++ break; ++ elem = elem->next; ++ } ++ if (path_end <= ep || !elem) ++ return elem; ++ while (*ep == '/' && ep < path_end) ++ ep++; ++ path = ep; ++ } ++ return NULL; ++} + +- if (slen < len) +- return -1; ++static struct tree_entry_list *find_entry(const char *path, ++ const char *path_end) ++{ ++ /* Find tree element, descending from root, that ++ * corresponds to the named path, lazily expanding ++ * the tree if possible. ++ */ ++ if (path == path_end) { ++ /* Special. This is the root level */ ++ return &root_entry; ++ } ++ return find_entry_0(&root_entry, path, path_end); ++} + +- if (strncmp(path,buff,len) == 0) { +- if (slen == len) +- return 0; +- else +- return 1; ++static void show_entry_name(struct tree_entry_list *e) ++{ ++ /* This is yucky. The root level is there for ++ * our convenience but we really want to do a ++ * forest. ++ */ ++ if (e->parent && e->parent != &root_entry) { ++ show_entry_name(e->parent); ++ putchar('/'); + } ++ printf("%s", e->name); ++} + +- return -1; +-} ++static const char *entry_type(struct tree_entry_list *e) ++{ ++ return (e->directory ? "tree" : "blob"); ++} + +-/* +- * match may be NULL, or a *sorted* list of paths +- */ +-static void list_recursive(void *buffer, +- const char *type, +- unsigned long size, +- struct path_prefix *prefix, +- char **match, int matches) +-{ +- struct path_prefix this_prefix; +- this_prefix.prev = prefix; +- +- if (strcmp(type, "tree")) +- die("expected a 'tree' node"); +- +- if (matches) +- recursive = 1; +- +- while (size) { +- int namelen = strlen(buffer)+1; +- void *eltbuf = NULL; +- char elttype[20]; +- unsigned long eltsize; +- unsigned char *sha1 = buffer + namelen; +- char *path = strchr(buffer, ' ') + 1; +- unsigned int mode; +- const char *matched = NULL; +- int mtype = -1; +- int mindex; +- +- if (size < namelen + 20 || sscanf(buffer, "%o", &mode) != 1) +- die("corrupt 'tree' file"); +- buffer = sha1 + 20; +- size -= namelen + 20; +- +- this_prefix.name = path; +- for ( mindex = 0; mindex < matches; mindex++) { +- mtype = pathcmp(match[mindex],&this_prefix); +- if (mtype >= 0) { +- matched = match[mindex]; +- break; +- } +- } ++static const char *entry_hex(struct tree_entry_list *e) ++{ ++ return sha1_to_hex(e->directory ++ ? e->item.tree->object.sha1 ++ : e->item.blob->object.sha1); ++} + +- /* +- * If we're not matching, or if this is an exact match, +- * print out the info +- */ +- if (!matches || (matched != NULL && mtype == 0)) { +- printf("%06o %s %s\t", mode, +- S_ISDIR(mode) ? "tree" : "blob", +- sha1_to_hex(sha1)); +- print_path_prefix(&this_prefix); +- putchar(line_termination); +- } ++/* forward declaration for mutually recursive routines */ ++static int show_entry(struct tree_entry_list *, int); + +- if (! recursive || ! S_ISDIR(mode)) +- continue; ++static int show_children(struct tree_entry_list *e, int level) ++{ ++ if (prepare_children(e)) ++ die("internal error: ls-tree show_children called with non tree"); ++ e = e->item.tree->entries; ++ while (e) { ++ show_entry(e, level); ++ e = e->next; ++ } ++ return 0; ++} + +- if (matches && ! matched) +- continue; ++static int show_entry(struct tree_entry_list *e, int level) ++{ ++ int err = 0; + +- if (! (eltbuf = read_sha1_file(sha1, elttype, &eltsize)) ) { +- error("cannot read %s", sha1_to_hex(sha1)); +- continue; +- } ++ if (e != &root_entry) { ++ printf("%06o %s %s ", e->mode, entry_type(e), ++ entry_hex(e)); ++ show_entry_name(e); ++ putchar(line_termination); ++ } + +- /* If this is an exact directory match, we may have +- * directory files following this path. Match on them. +- * Otherwise, we're at a pach subcomponent, and we need +- * to try to match again. ++ if (e->directory) { ++ /* If this is a directory, we have the following cases: ++ * (1) This is the top-level request (explicit path from the ++ * command line, or "root" if there is no command line). ++ * a. Without any flag. We show direct children. We do not ++ * recurse into them. ++ * b. With -r. We do recurse into children. ++ * c. With -d. We do not recurse into children. ++ * (2) We came here because our caller is either (1-a) or ++ * (1-b). ++ * a. Without any flag. We do not show our children (which ++ * are grandchildren for the original request). ++ * b. With -r. We continue to recurse into our children. ++ * c. With -d. We should not have come here to begin with. + */ +- if (mtype == 0) +- mindex++; +- +- list_recursive(eltbuf, elttype, eltsize, &this_prefix, &match[mindex], matches-mindex); +- free(eltbuf); ++ if (level == 0 && !(ls_options & LS_TREE_ONLY)) ++ /* case (1)-a and (1)-b */ ++ err = err | show_children(e, level+1); ++ else if (level && ls_options & LS_RECURSIVE) ++ /* case (2)-b */ ++ err = err | show_children(e, level+1); + } ++ return err; + } + +-static int qcmp(const void *a, const void *b) ++static int list_one(const char *path, const char *path_end) + { +- return strcmp(*(char **)a, *(char **)b); ++ int err = 0; ++ struct tree_entry_list *e = find_entry(path, path_end); ++ if (!e) { ++ /* traditionally ls-tree does not complain about ++ * missing path. We may change this later to match ++ * what "/bin/ls -a" does, which is to complain. ++ */ ++ return err; ++ } ++ err = err | show_entry(e, 0); ++ return err; + } + +-static int list(unsigned char *sha1,char **path) ++static int list(char **path) + { +- void *buffer; +- unsigned long size; +- int npaths; +- +- for (npaths = 0; path[npaths] != NULL; npaths++) +- ; +- +- qsort(path,npaths,sizeof(char *),qcmp); +- +- buffer = read_object_with_reference(sha1, "tree", &size, NULL); +- if (!buffer) +- die("unable to read sha1 file"); +- list_recursive(buffer, "tree", size, NULL, path, npaths); +- free(buffer); +- return 0; ++ int i; ++ int err = 0; ++ for (i = 0; path[i]; i++) { ++ int len = strlen(path[i]); ++ while (0 <= len && path[i][len] == '/') ++ len--; ++ err = err | list_one(path[i], path[i] + len); ++ } ++ return err; + } + +-static const char *ls_tree_usage = "git-ls-tree [-r] [-z] [paths...]"; ++static const char *ls_tree_usage = ++ "git-ls-tree [-d] [-r] [-z] [path...]"; + + int main(int argc, char **argv) + { ++ static char *path0[] = { "", NULL }; ++ char **path; + unsigned char sha1[20]; + + while (1 < argc && argv[1][0] == '-') { +@@ -194,7 +223,10 @@ int main(int argc, char **argv) + line_termination = 0; + break; + case 'r': +- recursive = 1; ++ ls_options |= LS_RECURSIVE; ++ break; ++ case 'd': ++ ls_options |= LS_TREE_ONLY; + break; + default: + usage(ls_tree_usage); +@@ -206,7 +238,10 @@ int main(int argc, char **argv) + usage(ls_tree_usage); + if (get_sha1(argv[1], sha1) < 0) + usage(ls_tree_usage); +- if (list(sha1, &argv[2]) < 0) ++ ++ path = (argc == 2) ? path0 : (argv + 2); ++ prepare_root(sha1); ++ if (list(path) < 0) + die("list failed"); + return 0; + } +diff a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh +--- a/t/t3100-ls-tree-restrict.sh ++++ b/t/t3100-ls-tree-restrict.sh +@@ -74,8 +74,8 @@ test_expect_success \ + 'ls-tree filtered' \ + 'git-ls-tree $tree path1 path0 >current && + cat >expected <<\EOF && +-100644 blob X path0 + 120000 blob X path1 ++100644 blob X path0 + EOF + test_output' + +@@ -85,7 +85,6 @@ test_expect_success \ + cat >expected <<\EOF && + 040000 tree X path2 + 040000 tree X path2/baz +-100644 blob X path2/baz/b + 120000 blob X path2/bazbo + 100644 blob X path2/foo + EOF +diff a/tree.c b/tree.c +--- a/tree.c ++++ b/tree.c +@@ -133,7 +133,7 @@ int parse_tree_buffer(struct tree *item, + } + if (obj) + add_ref(&item->object, obj); +- ++ entry->parent = NULL; /* needs to be filled by the user */ + *list_p = entry; + list_p = &entry->next; + } +diff a/tree.h b/tree.h +--- a/tree.h ++++ b/tree.h +@@ -16,6 +16,7 @@ struct tree_entry_list { + struct tree *tree; + struct blob *blob; + } item; ++ struct tree_entry_list *parent; + }; + + struct tree { -- cgit v0.10.2-6-g49f6