* [PATCH 2/6] Documentation: git-peek-remote.
From: Junio C Hamano @ 2005-07-24 0:54 UTC (permalink / raw)
To: Linus Torvalds; +Cc: git
Add documentation for the git-peek-remote and link it from the
main index.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
Documentation/git-peek-remote.txt | 53 +++++++++++++++++++++++++++++++++++++
Documentation/git.txt | 9 ++++--
2 files changed, 59 insertions(+), 3 deletions(-)
create mode 100644 Documentation/git-peek-remote.txt
6ad3ff7ed542308b4fce951affde15ffc3fcf690
diff --git a/Documentation/git-peek-remote.txt b/Documentation/git-peek-remote.txt
new file mode 100644
--- /dev/null
+++ b/Documentation/git-peek-remote.txt
@@ -0,0 +1,53 @@
+git-peek-remote(1)
+==================
+v0.1, July 2005
+
+NAME
+----
+git-peek-remote - Lists the references on a remote repository.
+
+
+SYNOPSIS
+--------
+'git-peek-remote' [--exec=<git-upload-pack>] [<host>:]<directory>
+
+DESCRIPTION
+-----------
+Lists the references the remote repository has, and optionally
+stores them in the local repository under the same name.
+
+OPTIONS
+-------
+--exec=<git-upload-pack>::
+ Use this to specify the path to 'git-upload-pack' on the
+ remote side, if is not found on your $PATH.
+ Installations of sshd ignores the user's environment
+ setup scripts for login shells (e.g. .bash_profile) and
+ your privately installed GIT may not be found on the system
+ default $PATH. Another workaround suggested is to set
+ up your $PATH in ".bashrc", but this flag is for people
+ who do not want to pay the overhead for non-interactive
+ shells by having a lean .bashrc file (they set most of
+ the things up in .bash_profile).
+
+<host>::
+ A remote host that houses the repository. When this
+ part is specified, 'git-upload-pack' is invoked via
+ ssh.
+
+<directory>::
+ The repository to sync from.
+
+
+Author
+------
+Written by Junio C Hamano <junkio@cox.net>
+
+Documentation
+--------------
+Documentation by Junio C Hamano.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git.txt b/Documentation/git.txt
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -131,12 +131,12 @@ link:git-clone-pack.html[git-clone-pack]
Clones a repository into the current repository (engine
for ssh and local transport)
-link:git-fetch-script.html[git-pull-script]::
- Pull from a repote repository via various protocols
+link:git-fetch-script.html[git-fetch-script]::
+ Download from a remote repository via various protocols
(user interface).
link:git-pull-script.html[git-pull-script]::
- Fetch from and merge with a repote repository via
+ Fetch from and merge with a remote repository via
various protocols (user interface).
link:git-http-pull.html[git-http-pull]::
@@ -160,6 +160,9 @@ link:git-clone-pack.html[git-clone-pack]
link:git-fetch-pack.html[git-fetch-pack]::
Updates from a remote repository.
+link:git-peek-remote.html[git-peek-remote]::
+ Lists references on a remote repository using upload-pack protocol.
+
link:git-upload-pack.html[git-upload-pack]::
Invoked by 'git-clone-pack' and 'git-fetch-pack' to push
what are asked for.
^ permalink raw reply
* [PATCH 3/6] git-ls-remote: show and optionally store remote refs.
From: Junio C Hamano @ 2005-07-24 0:54 UTC (permalink / raw)
To: Linus Torvalds; +Cc: git
Retrieve and list the remote refs from git, http, and rsync
repositories, and optionally stores the retrieved refs in the
local repository under the same name.
To access a git URL, git-peek-remote command is used. An http
URL needs to have an up-to-date info/refs file for discovery,
which will be introduced by a later update-server-info patch.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
Makefile | 3 +
git-ls-remote-script | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 106 insertions(+), 1 deletions(-)
create mode 100755 git-ls-remote-script
cb508fd7eb23a40432231977a9e7f12eb8d3e9de
diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -36,7 +36,8 @@ SCRIPTS=git git-apply-patch-script git-m
git-reset-script git-add-script git-checkout-script git-clone-script \
gitk git-cherry git-rebase-script git-relink-script git-repack-script \
git-format-patch-script git-sh-setup-script git-push-script \
- git-branch-script git-parse-remote git-verify-tag-script
+ git-branch-script git-parse-remote git-verify-tag-script \
+ git-ls-remote-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-ls-remote-script b/git-ls-remote-script
new file mode 100755
--- /dev/null
+++ b/git-ls-remote-script
@@ -0,0 +1,104 @@
+#!/bin/sh
+#
+. git-sh-setup-script || die "Not a git archive"
+
+usage () {
+ echo >&2 "usage: $0 [--heads] [--tags] [--overwrite | --store] repo"
+ exit 1;
+}
+
+while case "$#" in 0) break;; esac
+do
+ case "$1" in
+ -h|--h|--he|--hea|--head|--heads)
+ heads=heads; shift ;;
+ -o|--o|--ov|--ove|--over|--overw|--overwr|--overwri|--overwrit|--overwrite)
+ overwrite=overwrite; shift ;;
+ -s|--s|--st|--sto|--stor|--store)
+ store=store; shift ;;
+ -t|--t|--ta|--tag|--tags)
+ tags=tags; shift ;;
+ --)
+ shift; break ;;
+ -*)
+ usage ;;
+ *)
+ break ;;
+ esac
+done
+
+case "$#" in 1) ;; *) usage ;; esac
+case ",$store,$overwrite," in *,,*) ;; *) usage ;; esac
+
+case ",$heads,$tags," in
+,,,) heads=heads tags=tags other=other ;;
+esac
+
+. git-parse-remote "$@"
+peek_repo="$_remote_repo"
+
+tmp=.ls-remote-$$
+trap "rm -fr $tmp-*" 0 1 2 3 15
+tmpdir=$tmp-d
+
+case "$peek_repo" in
+http://* | https://* )
+ if [ -n "$GIT_SSL_NO_VERIFY" ]; then
+ curl_extra_args="-k"
+ fi
+ curl -ns $curl_extra_args "$peek_repo/info/refs" || exit 1
+ ;;
+
+rsync://* )
+ mkdir $tmpdir
+ rsync -rq "$peek_repo/refs" $tmpdir || exit 1
+ (cd $tmpdir && find refs -type f) |
+ while read path
+ do
+ cat "$tmpdir/$path" | tr -d '\012'
+ echo " $path"
+ done &&
+ rm -fr $tmpdir
+ ;;
+
+* )
+ git-peek-remote "$peek_repo"
+ ;;
+esac |
+
+while read sha1 path
+do
+ case "$path" in
+ refs/heads/*)
+ group=heads ;;
+ refs/tags/*)
+ group=tags ;;
+ *)
+ group=other ;;
+ esac
+ case ",$heads,$tags,$other," in
+ *,$group,*)
+ ;;
+ *)
+ continue;;
+ esac
+
+ echo "$sha1 $path"
+
+ case "$path,$store,$overwrite," in
+ *,,, | HEAD,*) continue ;;
+ esac
+
+ if test -f "$GIT_DIR/$path" && test "$overwrite" == ""
+ then
+ continue
+ fi
+
+ # Be careful. We may not have that object yet!
+ if git-cat-file -t "$sha1" >/dev/null 2>&1
+ then
+ echo "$sha1" >"$GIT_DIR/$path"
+ else
+ echo >&2 "* You have not fetched updated $path ($sha1)."
+ fi
+done
^ permalink raw reply
* [PATCH 4/6] Add update-server-info.
From: Junio C Hamano @ 2005-07-24 0:54 UTC (permalink / raw)
To: Linus Torvalds; +Cc: git
The git-update-server-info command prepares informational files
to help clients discover the contents of a repository, and pull
from it via a dumb transport protocols. Currently, the
following files are produced.
- The $repo/info/refs file lists the name of heads and tags
available in the $repo/refs/ directory, along with their
SHA1. This can be used by git-ls-remote command running on
the client side.
- The $repo/info/rev-cache file describes the commit ancestry
reachable from references in the $repo/refs/ directory. This
file is in an append-only binary format to make the server
side friendly to rsync mirroring scheme, and can be read by
git-show-rev-cache command.
- The $repo/objects/info/pack file lists the name of the packs
available, the interdependencies among them, and the head
commits and tags contained in them. Along with the other two
files, this is designed to help clients to make smart pull
decisions.
The git-receive-pack command is changed to invoke it at the end,
so just after a push to a public repository finishes via "git
push", the server info is automatically updated.
In addition, building of the rev-cache file can be done by a
standalone git-build-rev-cache command separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
Makefile | 11 +
build-rev-cache.c | 56 +++++
cache.h | 3
receive-pack.c | 1
rev-cache.c | 320 ++++++++++++++++++++++++++++
rev-cache.h | 29 +++
server-info.c | 565 ++++++++++++++++++++++++++++++++++++++++++++++++++
show-rev-cache.c | 18 ++
update-server-info.c | 23 ++
9 files changed, 1025 insertions(+), 1 deletions(-)
create mode 100644 build-rev-cache.c
create mode 100644 rev-cache.c
create mode 100644 rev-cache.h
create mode 100644 server-info.c
create mode 100644 show-rev-cache.c
create mode 100644 update-server-info.c
895f412b3df0a9a4884db6231bb590fd1e388db7
diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -50,7 +50,8 @@ PROG= git-update-cache git-diff-files
git-diff-stages git-rev-parse git-patch-id git-pack-objects \
git-unpack-objects git-verify-pack git-receive-pack git-send-pack \
git-prune-packed git-fetch-pack git-upload-pack git-clone-pack \
- git-show-index git-daemon git-var git-peek-remote
+ git-show-index git-daemon git-var git-peek-remote \
+ git-update-server-info git-show-rev-cache git-build-rev-cache
all: $(PROG)
@@ -65,6 +66,9 @@ LIB_FILE=libgit.a
LIB_H=cache.h object.h blob.h tree.h commit.h tag.h delta.h epoch.h csum-file.h \
pack.h pkt-line.h refs.h
+LIB_H += rev-cache.h
+LIB_OBJS += rev-cache.o
+
LIB_H += strbuf.h
LIB_OBJS += strbuf.o
@@ -76,6 +80,7 @@ LIB_OBJS += diff.o diffcore-rename.o dif
count-delta.o diffcore-break.o diffcore-order.o
LIB_OBJS += gitenv.o
+LIB_OBJS += server-info.o
LIBS = $(LIB_FILE)
LIBS += -lz
@@ -152,6 +157,9 @@ git-prune-packed: prune-packed.c
git-fetch-pack: fetch-pack.c
git-var: var.c
git-peek-remote: peek-remote.c
+git-update-server-info: update-server-info.c
+git-build-rev-cache: build-rev-cache.c
+git-show-rev-cache: show-rev-cache.c
git-http-pull: LIBS += -lcurl
git-rev-list: LIBS += -lssl
@@ -165,6 +173,7 @@ object.o: $(LIB_H)
read-cache.o: $(LIB_H)
sha1_file.o: $(LIB_H)
usage.o: $(LIB_H)
+rev-cache.o: $(LIB_H)
strbuf.o: $(LIB_H)
gitenv.o: $(LIB_H)
entry.o: $(LIB_H)
diff --git a/build-rev-cache.c b/build-rev-cache.c
new file mode 100644
--- /dev/null
+++ b/build-rev-cache.c
@@ -0,0 +1,56 @@
+#include "refs.h"
+#include "cache.h"
+#include "commit.h"
+#include "rev-cache.h"
+
+static void process_head_list(int verbose)
+{
+ char buf[512];
+
+ while (fgets(buf, sizeof(buf), stdin)) {
+ unsigned char sha1[20];
+ struct commit *commit;
+
+ if (get_sha1_hex(buf, sha1)) {
+ error("ignoring: %s", buf);
+ continue;
+ }
+ if (!(commit = lookup_commit_reference(sha1))) {
+ error("not a commit: %s", sha1_to_hex(sha1));
+ continue;
+ }
+ record_rev_cache(commit->object.sha1, verbose ? stderr : NULL);
+ }
+}
+
+
+static const char *build_rev_cache_usage =
+"git-build-rev-cache <rev-cache-file> < list-of-heads";
+
+int main(int ac, char **av)
+{
+ int verbose = 0;
+ const char *path;
+
+ while (1 < ac && av[1][0] == '-') {
+ if (!strcmp(av[1], "-v"))
+ verbose = 1;
+ else
+ usage(build_rev_cache_usage);
+ ac--; av++;
+ }
+
+ if (ac != 2)
+ usage(build_rev_cache_usage);
+
+ path = av[1];
+
+ /* read existing rev-cache */
+ read_rev_cache(path, NULL, 0);
+
+ process_head_list(verbose);
+
+ /* update the rev-cache database by appending newly found one to it */
+ write_rev_cache(path, path);
+ return 0;
+}
diff --git a/cache.h b/cache.h
--- a/cache.h
+++ b/cache.h
@@ -308,4 +308,7 @@ extern int find_pack_entry_one(const uns
extern void *unpack_entry_gently(struct pack_entry *, char *, unsigned long *);
extern void packed_object_info_detail(struct pack_entry *, char *, unsigned long *, unsigned long *, int *, unsigned char *);
+/* Dumb servers support */
+extern int update_server_info(int);
+
#endif /* CACHE_H */
diff --git a/receive-pack.c b/receive-pack.c
--- a/receive-pack.c
+++ b/receive-pack.c
@@ -110,6 +110,7 @@ static void execute_commands(void)
update(cmd->ref_name, cmd->old_sha1, cmd->new_sha1);
cmd = cmd->next;
}
+ update_server_info(0);
}
static void read_head_info(void)
diff --git a/rev-cache.c b/rev-cache.c
new file mode 100644
--- /dev/null
+++ b/rev-cache.c
@@ -0,0 +1,320 @@
+#include "refs.h"
+#include "cache.h"
+#include "rev-cache.h"
+
+struct rev_cache **rev_cache;
+int nr_revs, alloc_revs;
+
+struct rev_list_elem *rle_free;
+
+#define BATCH_SIZE 512
+
+int find_rev_cache(const unsigned char *sha1)
+{
+ int lo = 0, hi = nr_revs;
+ while (lo < hi) {
+ int mi = (lo + hi) / 2;
+ struct rev_cache *ri = rev_cache[mi];
+ int cmp = memcmp(sha1, ri->sha1, 20);
+ if (!cmp)
+ return mi;
+ if (cmp < 0)
+ hi = mi;
+ else
+ lo = mi + 1;
+ }
+ return -lo - 1;
+}
+
+static struct rev_list_elem *alloc_list_elem(void)
+{
+ struct rev_list_elem *rle;
+ if (!rle_free) {
+ int i;
+
+ rle = xmalloc(sizeof(*rle) * BATCH_SIZE);
+ for (i = 0; i < BATCH_SIZE - 1; i++) {
+ rle[i].ri = NULL;
+ rle[i].next = &rle[i + 1];
+ }
+ rle[BATCH_SIZE - 1].ri = NULL;
+ rle[BATCH_SIZE - 1].next = NULL;
+ rle_free = rle;
+ }
+ rle = rle_free;
+ rle_free = rle->next;
+ return rle;
+}
+
+static struct rev_cache *create_rev_cache(const unsigned char *sha1)
+{
+ struct rev_cache *ri;
+ int pos = find_rev_cache(sha1);
+
+ if (0 <= pos)
+ return rev_cache[pos];
+ pos = -pos - 1;
+ if (alloc_revs <= ++nr_revs) {
+ alloc_revs = alloc_nr(alloc_revs);
+ rev_cache = xrealloc(rev_cache, sizeof(ri) * alloc_revs);
+ }
+ if (pos < nr_revs)
+ memmove(rev_cache + pos + 1, rev_cache + pos,
+ (nr_revs - pos - 1) * sizeof(ri));
+ ri = xcalloc(1, sizeof(*ri));
+ memcpy(ri->sha1, sha1, 20);
+ rev_cache[pos] = ri;
+ return ri;
+}
+
+static unsigned char last_sha1[20];
+
+static void write_one_rev_cache(FILE *rev_cache_file, struct rev_cache *ri)
+{
+ unsigned char flag;
+ struct rev_list_elem *rle;
+
+ if (ri->written)
+ return;
+
+ if (ri->parsed) {
+ /* We use last_sha1 compression only for the first parent;
+ * otherwise the resulting rev-cache would lose the parent
+ * order information.
+ */
+ if (ri->parents &&
+ !memcmp(ri->parents->ri->sha1, last_sha1, 20))
+ flag = (ri->num_parents - 1) | 0x80;
+ else
+ flag = ri->num_parents;
+
+ fwrite(ri->sha1, 20, 1, rev_cache_file);
+ fwrite(&flag, 1, 1, rev_cache_file);
+ for (rle = ri->parents; rle; rle = rle->next) {
+ if (flag & 0x80 && rle == ri->parents)
+ continue;
+ fwrite(rle->ri->sha1, 20, 1, rev_cache_file);
+ }
+ memcpy(last_sha1, ri->sha1, 20);
+ ri->written = 1;
+ }
+ /* recursively write children depth first */
+ for (rle = ri->children; rle; rle = rle->next)
+ write_one_rev_cache(rev_cache_file, rle->ri);
+}
+
+void write_rev_cache(const char *newpath, const char *oldpath)
+{
+ /* write the following commit ancestry information in
+ * $GIT_DIR/info/rev-cache.
+ *
+ * The format is:
+ * 20-byte SHA1 (commit ID)
+ * 1-byte flag:
+ * - bit 0-6 records "number of parent commit SHA1s to
+ * follow" (i.e. up to 127 children can be listed).
+ * - when the bit 7 is on, then "the entry immediately
+ * before this entry is one of the parents of this
+ * commit".
+ * N x 20-byte SHA1 (parent commit IDs)
+ */
+ FILE *rev_cache_file;
+ int i;
+ struct rev_cache *ri;
+
+ if (!strcmp(newpath, oldpath)) {
+ /* If we are doing it in place */
+ rev_cache_file = fopen(newpath, "a");
+ }
+ else {
+ char buf[8096];
+ size_t sz;
+ FILE *oldfp = fopen(oldpath, "r");
+ rev_cache_file = fopen(newpath, "w");
+ if (oldfp) {
+ while (1) {
+ sz = fread(buf, 1, sizeof(buf), oldfp);
+ if (sz == 0)
+ break;
+ fwrite(buf, 1, sz, rev_cache_file);
+ }
+ fclose(oldfp);
+ }
+ }
+
+ memset(last_sha1, 0, 20);
+
+ /* Go through available rev_cache structures, starting from
+ * parentless ones first, so that we would get most out of
+ * last_sha1 optimization by the depth first behaviour of
+ * write_one_rev_cache().
+ */
+ for (i = 0; i < nr_revs; i++) {
+ ri = rev_cache[i];
+ if (ri->num_parents)
+ continue;
+ write_one_rev_cache(rev_cache_file, ri);
+ }
+ /* Then the rest */
+ for (i = 0; i < nr_revs; i++) {
+ ri = rev_cache[i];
+ write_one_rev_cache(rev_cache_file, ri);
+ }
+ fclose(rev_cache_file);
+}
+
+static void add_parent(struct rev_cache *child,
+ const unsigned char *parent_sha1)
+{
+ struct rev_cache *parent = create_rev_cache(parent_sha1);
+ struct rev_list_elem *e = alloc_list_elem();
+
+ /* Keep the parent list ordered in the same way the commit
+ * object records them.
+ */
+ e->ri = parent;
+ e->next = NULL;
+ if (!child->parents_tail)
+ child->parents = e;
+ else
+ child->parents_tail->next = e;
+ child->parents_tail = e;
+ child->num_parents++;
+
+ /* There is no inherent order of the children so we just
+ * LIFO them together.
+ */
+ e = alloc_list_elem();
+ e->next = parent->children;
+ parent->children = e;
+ e->ri = child;
+ parent->num_children++;
+}
+
+int read_rev_cache(const char *path, FILE *dumpfile, int dry_run)
+{
+ unsigned char *map;
+ int fd;
+ struct stat st;
+ unsigned long ofs, len;
+ struct rev_cache *ri = NULL;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ if (dry_run)
+ return error("cannot open %s", path);
+ if (errno == ENOENT)
+ return 0;
+ return -1;
+ }
+ if (fstat(fd, &st)) {
+ close(fd);
+ return -1;
+ }
+ map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (map == MAP_FAILED) {
+ close(fd);
+ return -1;
+ }
+ close(fd);
+
+ memset(last_sha1, 0, 20);
+ ofs = 0;
+ len = st.st_size;
+ while (ofs < len) {
+ unsigned char sha1[20];
+ int flag, cnt, i;
+ if (len < ofs + 21)
+ die("rev-cache too short");
+ memcpy(sha1, map + ofs, 20);
+ flag = map[ofs + 20];
+ ofs += 21;
+ cnt = (flag & 0x7f) + ((flag & 0x80) != 0);
+ if (len < ofs + (flag & 0x7f) * 20)
+ die("rev-cache too short to have %d more parents",
+ (flag & 0x7f));
+ if (dumpfile)
+ fprintf(dumpfile, "%s", sha1_to_hex(sha1));
+ if (!dry_run) {
+ ri = create_rev_cache(sha1);
+ if (!ri)
+ die("cannot create rev-cache for %s",
+ sha1_to_hex(sha1));
+ ri->written = ri->parsed = 1;
+ }
+ i = 0;
+ if (flag & 0x80) {
+ if (!dry_run)
+ add_parent(ri, last_sha1);
+ if (dumpfile)
+ fprintf(dumpfile, " %s",
+ sha1_to_hex(last_sha1));
+ i++;
+ }
+ while (i++ < cnt) {
+ if (!dry_run)
+ add_parent(ri, map + ofs);
+ if (dumpfile)
+ fprintf(dumpfile, " %s",
+ sha1_to_hex(last_sha1));
+ ofs += 20;
+ }
+ if (dumpfile)
+ fprintf(dumpfile, "\n");
+ memcpy(last_sha1, sha1, 20);
+ }
+ if (ofs != len)
+ die("rev-cache truncated?");
+ munmap(map, len);
+ return 0;
+}
+
+int record_rev_cache(const unsigned char *sha1, FILE *dumpfile)
+{
+ unsigned char parent[20];
+ char type[20];
+ unsigned long size, ofs;
+ unsigned int cnt, i;
+ void *buf;
+ struct rev_cache *ri;
+
+ buf = read_sha1_file(sha1, type, &size);
+ if (!buf)
+ return error("%s: not found", sha1_to_hex(sha1));
+ if (strcmp(type, "commit")) {
+ free(buf);
+ return error("%s: not a commit but a %s",
+ sha1_to_hex(sha1), type);
+ }
+ ri = create_rev_cache(sha1);
+ if (ri->parsed)
+ return 0;
+ if (dumpfile)
+ fprintf(dumpfile, "commit %s\n", sha1_to_hex(sha1));
+
+ cnt = 0;
+ ofs = 46; /* "tree " + hex-sha1 + "\n" */
+ while (!memcmp(buf + ofs, "parent ", 7) &&
+ !get_sha1_hex(buf + ofs + 7, parent)) {
+ ofs += 48;
+ cnt++;
+ }
+ if (cnt * 48 + 46 != ofs) {
+ free(buf);
+ die("internal error in record_rev_cache");
+ }
+
+ ri = create_rev_cache(sha1);
+ ri->parsed = 1;
+
+ for (i = 0; i < cnt; i++) {
+ unsigned char parent_sha1[20];
+
+ ofs = 46 + i * 48 + 7;
+ get_sha1_hex(buf + ofs, parent_sha1);
+ add_parent(ri, parent_sha1);
+ record_rev_cache(parent_sha1, dumpfile);
+ }
+ free(buf);
+ return 0;
+}
diff --git a/rev-cache.h b/rev-cache.h
new file mode 100644
--- /dev/null
+++ b/rev-cache.h
@@ -0,0 +1,29 @@
+#ifndef REV_CACHE_H
+#define REV_CACHE_H
+
+extern struct rev_cache {
+ struct rev_cache *head_list;
+ struct rev_list_elem *children;
+ struct rev_list_elem *parents;
+ struct rev_list_elem *parents_tail;
+ unsigned short num_parents;
+ unsigned short num_children;
+ unsigned int written : 1;
+ unsigned int parsed : 1;
+ unsigned int work : 30;
+ void *work_ptr;
+ unsigned char sha1[20];
+} **rev_cache;
+extern int nr_revs, alloc_revs;
+
+struct rev_list_elem {
+ struct rev_list_elem *next;
+ struct rev_cache *ri;
+};
+
+extern int find_rev_cache(const unsigned char *);
+extern int read_rev_cache(const char *, FILE *, int);
+extern int record_rev_cache(const unsigned char *, FILE *);
+extern void write_rev_cache(const char *new, const char *old);
+
+#endif
diff --git a/server-info.c b/server-info.c
new file mode 100644
--- /dev/null
+++ b/server-info.c
@@ -0,0 +1,565 @@
+#include "cache.h"
+#include "refs.h"
+#include "object.h"
+#include "commit.h"
+#include "rev-cache.h"
+
+/* refs */
+static FILE *info_ref_fp;
+static unsigned long info_ref_time;
+static int info_ref_is_stale = 0;
+
+static int stat_ref(const char *path, const unsigned char *sha1)
+{
+ struct stat st;
+ if (!stat(path, &st) && info_ref_time < st.st_mtime)
+ info_ref_is_stale = 1;
+ return 0;
+}
+
+static int add_info_ref(const char *path, const unsigned char *sha1)
+{
+ fprintf(info_ref_fp, "%s %s\n", sha1_to_hex(sha1), path);
+ return 0;
+}
+
+static int update_info_refs(int force)
+{
+ struct stat st;
+ char *path0 = strdup(git_path("info/refs"));
+ int len = strlen(path0);
+ char *path1 = xmalloc(len + 2);
+
+ strcpy(path1, path0);
+ strcpy(path1 + len, "+");
+
+ if (!force) {
+ if (stat(path0, &st)) {
+ if (errno == ENOENT)
+ info_ref_is_stale = 1;
+ else
+ return error("cannot stat %s", path0);
+ }
+ else {
+ info_ref_time = st.st_mtime;
+ for_each_ref(stat_ref);
+ }
+ if (!info_ref_is_stale)
+ return 0;
+ }
+
+ safe_create_leading_directories(path0);
+ info_ref_fp = fopen(path1, "w");
+ if (!info_ref_fp)
+ return error("unable to update %s", path0);
+ for_each_ref(add_info_ref);
+ fclose(info_ref_fp);
+ rename(path1, path0);
+ free(path0);
+ free(path1);
+ return 0;
+}
+
+/* packs */
+struct pack_info {
+ unsigned long latest;
+ struct packed_git *p;
+ int old_num;
+ int new_num;
+ int nr_alloc;
+ int nr_heads;
+ unsigned char (*head)[20];
+ char dep[0]; /* more */
+} **info;
+static int num_pack;
+static const char *objdir;
+static int objdirlen;
+
+static struct object *parse_object_cheap(const unsigned char *sha1)
+{
+ struct object *o;
+
+ if ((o = parse_object(sha1)) == NULL)
+ return NULL;
+ if (o->type == commit_type) {
+ struct commit *commit = (struct commit *)o;
+ free(commit->buffer);
+ commit->buffer = NULL;
+ }
+ return o;
+}
+
+static struct pack_info *find_pack_by_name(const char *name)
+{
+ int i;
+ for (i = 0; i < num_pack; i++) {
+ struct packed_git *p = info[i]->p;
+ /* skip "/pack/" after ".git/objects" */
+ if (!strcmp(p->pack_name + objdirlen + 6, name))
+ return info[i];
+ }
+ return NULL;
+}
+
+static struct pack_info *find_pack_by_old_num(int old_num)
+{
+ int i;
+ for (i = 0; i < num_pack; i++)
+ if (info[i]->old_num == old_num)
+ return info[i];
+ return NULL;
+}
+
+static int add_head_def(struct pack_info *this, unsigned char *sha1)
+{
+ if (this->nr_alloc <= this->nr_heads) {
+ this->nr_alloc = alloc_nr(this->nr_alloc);
+ this->head = xrealloc(this->head, this->nr_alloc * 20);
+ }
+ memcpy(this->head[this->nr_heads++], sha1, 20);
+ return 0;
+}
+
+/* Returns non-zero when we detect that the info in the
+ * old file is useless.
+ */
+static int parse_pack_def(const char *line, int old_cnt)
+{
+ struct pack_info *i = find_pack_by_name(line + 2);
+ if (i) {
+ i->old_num = old_cnt;
+ return 0;
+ }
+ else {
+ /* The file describes a pack that is no longer here;
+ * dependencies between packs needs to be recalculated.
+ */
+ return 1;
+ }
+}
+
+/* Returns non-zero when we detect that the info in the
+ * old file is useless.
+ */
+static int parse_depend_def(char *line)
+{
+ unsigned long num;
+ char *cp, *ep;
+ struct pack_info *this, *that;
+
+ cp = line + 2;
+ num = strtoul(cp, &ep, 10);
+ if (ep == cp)
+ return error("invalid input %s", line);
+ this = find_pack_by_old_num(num);
+ if (!this)
+ return 0;
+ while (ep && *(cp = ep)) {
+ num = strtoul(cp, &ep, 10);
+ if (ep == cp)
+ break;
+ that = find_pack_by_old_num(num);
+ if (!that)
+ /* The pack this one depends on does not
+ * exist; this should not happen because
+ * we write out the list of packs first and
+ * then dependency information, but it means
+ * the file is useless anyway.
+ */
+ return 1;
+ this->dep[that->new_num] = 1;
+ }
+ return 0;
+}
+
+/* Returns non-zero when we detect that the info in the
+ * old file is useless.
+ */
+static int parse_head_def(char *line)
+{
+ unsigned char sha1[20];
+ unsigned long num;
+ char *cp, *ep;
+ struct pack_info *this;
+ struct object *o;
+
+ cp = line + 2;
+ num = strtoul(cp, &ep, 10);
+ if (ep == cp || *ep++ != ' ')
+ return error("invalid input ix %s", line);
+ this = find_pack_by_old_num(num);
+ if (!this)
+ return 1; /* You know the drill. */
+ if (get_sha1_hex(ep, sha1) || ep[40] != ' ')
+ return error("invalid input sha1 %s (%s)", line, ep);
+ if ((o = parse_object_cheap(sha1)) == NULL)
+ return error("no such object: %s", line);
+ return add_head_def(this, sha1);
+}
+
+/* Returns non-zero when we detect that the info in the
+ * old file is useless.
+ */
+static int read_pack_info_file(const char *infofile)
+{
+ FILE *fp;
+ char line[1000];
+ int old_cnt = 0;
+
+ fp = fopen(infofile, "r");
+ if (!fp)
+ return 1; /* nonexisting is not an error. */
+
+ while (fgets(line, sizeof(line), fp)) {
+ int len = strlen(line);
+ if (line[len-1] == '\n')
+ line[len-1] = 0;
+
+ switch (line[0]) {
+ case 'P': /* P name */
+ if (parse_pack_def(line, old_cnt++))
+ goto out_stale;
+ break;
+ case 'D': /* D ix dep-ix1 dep-ix2... */
+ if (parse_depend_def(line))
+ goto out_stale;
+ break;
+ case 'T': /* T ix sha1 type */
+ if (parse_head_def(line))
+ goto out_stale;
+ break;
+ default:
+ error("unrecognized: %s", line);
+ break;
+ }
+ }
+ fclose(fp);
+ return 0;
+ out_stale:
+ fclose(fp);
+ return 1;
+}
+
+/* We sort the packs according to the date of the latest commit. That
+ * in turn indicates how young the pack is, and in general we would
+ * want to depend on younger packs.
+ */
+static unsigned long get_latest_commit_date(struct packed_git *p)
+{
+ unsigned char sha1[20];
+ struct object *o;
+ int num = num_packed_objects(p);
+ int i;
+ unsigned long latest = 0;
+
+ for (i = 0; i < num; i++) {
+ if (nth_packed_object_sha1(p, i, sha1))
+ die("corrupt pack file %s?", p->pack_name);
+ if ((o = parse_object_cheap(sha1)) == NULL)
+ die("cannot parse %s", sha1_to_hex(sha1));
+ if (o->type == commit_type) {
+ struct commit *commit = (struct commit *)o;
+ if (latest < commit->date)
+ latest = commit->date;
+ }
+ }
+ return latest;
+}
+
+static int compare_info(const void *a_, const void *b_)
+{
+ struct pack_info * const* a = a_;
+ struct pack_info * const* b = b_;
+
+ if (0 <= (*a)->old_num && 0 <= (*b)->old_num)
+ /* Keep the order in the original */
+ return (*a)->old_num - (*b)->old_num;
+ else if (0 <= (*a)->old_num)
+ /* Only A existed in the original so B is obviously newer */
+ return -1;
+ else if (0 <= (*b)->old_num)
+ /* The other way around. */
+ return 1;
+
+ if ((*a)->latest < (*b)->latest)
+ return -1;
+ else if ((*a)->latest == (*b)->latest)
+ return 0;
+ else
+ return 1;
+}
+
+static void init_pack_info(const char *infofile, int force)
+{
+ struct packed_git *p;
+ int stale;
+ int i = 0;
+ char *dep_temp;
+
+ objdir = get_object_directory();
+ objdirlen = strlen(objdir);
+
+ prepare_packed_git();
+ for (p = packed_git; p; p = p->next) {
+ /* we ignore things on alternate path since they are
+ * not available to the pullers in general.
+ */
+ if (strncmp(p->pack_name, objdir, objdirlen) ||
+ strncmp(p->pack_name + objdirlen, "/pack/", 6))
+ continue;
+ i++;
+ }
+ num_pack = i;
+ info = xcalloc(num_pack, sizeof(struct pack_info *));
+ for (i = 0, p = packed_git; p; p = p->next) {
+ if (strncmp(p->pack_name, objdir, objdirlen) ||
+ p->pack_name[objdirlen] != '/')
+ continue;
+ info[i] = xcalloc(1, sizeof(struct pack_info) + num_pack);
+ info[i]->p = p;
+ info[i]->old_num = -1;
+ i++;
+ }
+
+ if (infofile && !force)
+ stale = read_pack_info_file(infofile);
+ else
+ stale = 1;
+
+ for (i = 0; i < num_pack; i++) {
+ if (stale) {
+ info[i]->old_num = -1;
+ memset(info[i]->dep, 0, num_pack);
+ info[i]->nr_heads = 0;
+ }
+ if (info[i]->old_num < 0)
+ info[i]->latest = get_latest_commit_date(info[i]->p);
+ }
+
+ qsort(info, num_pack, sizeof(info[0]), compare_info);
+ for (i = 0; i < num_pack; i++)
+ info[i]->new_num = i;
+
+ /* we need to fix up the dependency information
+ * for the old ones.
+ */
+ dep_temp = NULL;
+ for (i = 0; i < num_pack; i++) {
+ int old;
+
+ if (info[i]->old_num < 0)
+ continue;
+ if (! dep_temp)
+ dep_temp = xmalloc(num_pack);
+ memset(dep_temp, 0, num_pack);
+ for (old = 0; old < num_pack; old++) {
+ struct pack_info *base;
+ if (!info[i]->dep[old])
+ continue;
+ base = find_pack_by_old_num(old);
+ if (!base)
+ die("internal error renumbering");
+ dep_temp[base->new_num] = 1;
+ }
+ memcpy(info[i]->dep, dep_temp, num_pack);
+ }
+ free(dep_temp);
+}
+
+static void write_pack_info_file(FILE *fp)
+{
+ int i, j;
+ for (i = 0; i < num_pack; i++)
+ fprintf(fp, "P %s\n", info[i]->p->pack_name + objdirlen + 6);
+
+ for (i = 0; i < num_pack; i++) {
+ fprintf(fp, "D %1d", i);
+ for (j = 0; j < num_pack; j++) {
+ if ((i == j) || !(info[i]->dep[j]))
+ continue;
+ fprintf(fp, " %1d", j);
+ }
+ fputc('\n', fp);
+ }
+
+ for (i = 0; i < num_pack; i++) {
+ struct pack_info *this = info[i];
+ for (j = 0; j < this->nr_heads; j++) {
+ struct object *o = lookup_object(this->head[j]);
+ fprintf(fp, "T %1d %s %s\n",
+ i, sha1_to_hex(this->head[j]), o->type);
+ }
+ }
+
+}
+
+#define REFERENCED 01
+#define INTERNAL 02
+#define EMITTED 04
+
+static void show(struct object *o, int pack_ix)
+{
+ /*
+ * We are interested in objects that are not referenced,
+ * and objects that are referenced but not internal.
+ */
+ if (o->flags & EMITTED)
+ return;
+
+ if (!(o->flags & REFERENCED))
+ add_head_def(info[pack_ix], o->sha1);
+ else if ((o->flags & REFERENCED) && !(o->flags & INTERNAL)) {
+ int i;
+
+ /* Which pack contains this object? That is what
+ * pack_ix can depend on. We earlier sorted info
+ * array from youngest to oldest, so try newer packs
+ * first to favor them here.
+ */
+ for (i = num_pack - 1; 0 <= i; i--) {
+ struct packed_git *p = info[i]->p;
+ struct pack_entry ent;
+ if (find_pack_entry_one(o->sha1, &ent, p)) {
+ info[pack_ix]->dep[i] = 1;
+ break;
+ }
+ }
+ }
+ o->flags |= EMITTED;
+}
+
+static void find_pack_info_one(int pack_ix)
+{
+ unsigned char sha1[20];
+ struct object *o;
+ struct object_list *ref;
+ int i;
+ struct packed_git *p = info[pack_ix]->p;
+ int num = num_packed_objects(p);
+
+ /* Scan objects, clear flags from all the edge ones and
+ * internal ones, possibly marked in the previous round.
+ */
+ for (i = 0; i < num; i++) {
+ if (nth_packed_object_sha1(p, i, sha1))
+ die("corrupt pack file %s?", p->pack_name);
+ if ((o = lookup_object(sha1)) == NULL)
+ die("cannot parse %s", sha1_to_hex(sha1));
+ for (ref = o->refs; ref; ref = ref->next)
+ ref->item->flags = 0;
+ o->flags = 0;
+ }
+
+ /* Mark all the internal ones */
+ for (i = 0; i < num; i++) {
+ if (nth_packed_object_sha1(p, i, sha1))
+ die("corrupt pack file %s?", p->pack_name);
+ if ((o = lookup_object(sha1)) == NULL)
+ die("cannot find %s", sha1_to_hex(sha1));
+ for (ref = o->refs; ref; ref = ref->next)
+ ref->item->flags |= REFERENCED;
+ o->flags |= INTERNAL;
+ }
+
+ for (i = 0; i < num; i++) {
+ if (nth_packed_object_sha1(p, i, sha1))
+ die("corrupt pack file %s?", p->pack_name);
+ if ((o = lookup_object(sha1)) == NULL)
+ die("cannot find %s", sha1_to_hex(sha1));
+
+ show(o, pack_ix);
+ for (ref = o->refs; ref; ref = ref->next)
+ show(ref->item, pack_ix);
+ }
+
+}
+
+static void find_pack_info(void)
+{
+ int i;
+ for (i = 0; i < num_pack; i++) {
+ /* The packed objects are cast in stone, and a head
+ * in a pack will stay as head, so is the set of missing
+ * objects. If the repo has been reorganized and we
+ * are missing some packs available back then, we have
+ * already discarded the info read from the file, so
+ * we will find (old_num < 0) in that case.
+ */
+ if (0 <= info[i]->old_num)
+ continue;
+ find_pack_info_one(i);
+ }
+}
+
+static int update_info_packs(int force)
+{
+ char infofile[PATH_MAX];
+ char name[PATH_MAX];
+ int namelen;
+ FILE *fp;
+
+ namelen = sprintf(infofile, "%s/info/packs", get_object_directory());
+ strcpy(name, infofile);
+ strcpy(name + namelen, "+");
+
+ init_pack_info(infofile, force);
+ find_pack_info();
+
+ safe_create_leading_directories(name);
+ fp = fopen(name, "w");
+ if (!fp)
+ return error("cannot open %s", name);
+ write_pack_info_file(fp);
+ fclose(fp);
+ rename(name, infofile);
+ return 0;
+}
+
+/* rev-cache */
+static int record_rev_cache_ref(const char *path, const unsigned char *sha1)
+{
+ struct commit *commit;
+ if (!(commit = lookup_commit_reference(sha1)))
+ return error("not a commit: %s", sha1_to_hex(sha1));
+ return record_rev_cache(commit->object.sha1, NULL);
+}
+
+static int update_info_revs(int force)
+{
+ char *path0 = strdup(git_path("info/rev-cache"));
+ int len = strlen(path0);
+ char *path1 = xmalloc(len + 2);
+
+ strcpy(path1, path0);
+ strcpy(path1 + len, "+");
+
+ /* read existing rev-cache */
+ if (!force)
+ read_rev_cache(path0, NULL, 0);
+ safe_create_leading_directories(path0);
+
+ for_each_ref(record_rev_cache_ref);
+
+ /* update the rev-cache database */
+ write_rev_cache(path1, force ? "/dev/null" : path0);
+ rename(path1, path0);
+ free(path1);
+ free(path0);
+ return 0;
+}
+
+/* public */
+int update_server_info(int force)
+{
+ /* We would add more dumb-server support files later,
+ * including index of available pack files and their
+ * intended audiences.
+ */
+ int errs = 0;
+
+ errs = errs | update_info_refs(force);
+ errs = errs | update_info_packs(force);
+ errs = errs | update_info_revs(force);
+
+ return errs;
+}
diff --git a/show-rev-cache.c b/show-rev-cache.c
new file mode 100644
--- /dev/null
+++ b/show-rev-cache.c
@@ -0,0 +1,18 @@
+#include "cache.h"
+#include "rev-cache.h"
+
+static char *show_rev_cache_usage =
+"git-show-rev-cache <rev-cache-file>";
+
+int main(int ac, char **av)
+{
+ while (1 < ac && av[0][1] == '-') {
+ /* do flags here */
+ break;
+ ac--; av++;
+ }
+ if (ac != 2)
+ usage(show_rev_cache_usage);
+
+ return read_rev_cache(av[1], stdout, 1);
+}
diff --git a/update-server-info.c b/update-server-info.c
new file mode 100644
--- /dev/null
+++ b/update-server-info.c
@@ -0,0 +1,23 @@
+#include "cache.h"
+
+static const char update_server_info_usage[] =
+"git-update-server-info [--force]";
+
+int main(int ac, char **av)
+{
+ int i;
+ int force = 0;
+ for (i = 1; i < ac; i++) {
+ if (av[i][0] == '-') {
+ if (!strcmp("--force", av[i]) ||
+ !strcmp("-f", av[i]))
+ force = 1;
+ else
+ usage(update_server_info_usage);
+ }
+ }
+ if (i != ac)
+ usage(update_server_info_usage);
+
+ return !!update_server_info(force);
+}
^ permalink raw reply
* [PATCH 5/6] Document update-server-info.
From: Junio C Hamano @ 2005-07-24 0:54 UTC (permalink / raw)
To: Linus Torvalds; +Cc: git
This adds a minimum documentation to the new command.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
Documentation/git-update-server-info.txt | 42 ++++++++++++++++++++++++++++++
Documentation/git.txt | 4 +++
2 files changed, 46 insertions(+), 0 deletions(-)
create mode 100644 Documentation/git-update-server-info.txt
b0dff8ec614449f3fa59a1017f3ed92c8a10a3aa
diff --git a/Documentation/git-update-server-info.txt b/Documentation/git-update-server-info.txt
new file mode 100644
--- /dev/null
+++ b/Documentation/git-update-server-info.txt
@@ -0,0 +1,42 @@
+git-update-server-info(1)
+=========================
+v0.1, July 2005
+
+NAME
+----
+git-update-server-info - Update auxiliary info file to help dumb servers
+
+
+SYNOPSIS
+--------
+'git-update-server-info' [--force]
+
+DESCRIPTION
+-----------
+A dumb server that does not do on-the-fly pack generations can
+have some auxiliary information files in $GIT_DIR/info and
+$GIT_OBJECT_DIRECTORY/info directories to help clients discover
+what references and packs the server has and make an optimized
+pull decisions. This command generates such auxiliary files.
+
+
+OPTIONS
+-------
+
+--force::
+ Update the info files even when they do not appear
+ stale.
+
+
+Author
+------
+Written by Junio C Hamano <junkio@cox.net>
+
+Documentation
+--------------
+Documentation by Junio C Hamano.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git.txt b/Documentation/git.txt
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -167,6 +167,10 @@ link:git-upload-pack.html[git-upload-pac
Invoked by 'git-clone-pack' and 'git-fetch-pack' to push
what are asked for.
+link:git-update-server-info.html[git-update-server-info]::
+ Updates auxiliary information on a dumb server to help
+ clients discover references and packs on it.
+
Ancilliary Commands
-------------------
^ permalink raw reply
* [PATCH 6/6] Support cloning packed repo from dumb http servers.
From: Junio C Hamano @ 2005-07-24 0:55 UTC (permalink / raw)
To: Linus Torvalds; +Cc: git
Using the information prepared with update-server-info, a truly
dumb http server can allow cloning with this client side
support.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
Makefile | 2 +-
git-clone-dumb-http | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
git-clone-script | 13 +++++++++++--
3 files changed, 63 insertions(+), 3 deletions(-)
create mode 100755 git-clone-dumb-http
5dd66390cbe5fe0665681ad0d73368085c87b01d
diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -37,7 +37,7 @@ SCRIPTS=git git-apply-patch-script git-m
gitk git-cherry git-rebase-script git-relink-script git-repack-script \
git-format-patch-script git-sh-setup-script git-push-script \
git-branch-script git-parse-remote git-verify-tag-script \
- git-ls-remote-script
+ git-ls-remote-script git-clone-dumb-http
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-clone-dumb-http b/git-clone-dumb-http
new file mode 100755
--- /dev/null
+++ b/git-clone-dumb-http
@@ -0,0 +1,51 @@
+#!/bin/sh
+#
+# Copyright (c) 2005, Junio C Hamano
+#
+# Called by git-clone-script
+# Exits 2 when the remote site does not support dumb server protocol.
+
+# Usage: git-clone-dumb-http <remote-repo> <local-dir>
+
+R=${1?"remote repository"} D=${2?"local directory"}
+
+if [ -n "$GIT_SSL_NO_VERIFY" ]; then
+ curl_extra_args="-k"
+fi
+http_fetch () {
+ # $1 = Remote, $2 = Local
+ curl -ns $curl_extra_args "$1" >"$2"
+}
+
+cd "$D" &&
+clone_tmp=".git/clone-tmp" &&
+mkdir -p "$clone_tmp" || exit 1
+trap "rm -rf .git/clone-tmp" 0 1 2 3 15
+
+http_fetch "$R/info/refs" "$clone_tmp/refs" &&
+http_fetch "$R/objects/info/packs" "$clone_tmp/packs" || exit 2
+
+# We do not have to worry about rev-cache when cloning.
+# http_fetch "$R/info/rev-cache" "$clone_tmp/rev-cache"
+
+# Clone packs
+while read type name
+do
+ case "$type" in
+ P) ;;
+ *) continue ;;
+ esac &&
+
+ idx=`expr "$name" : '\(.*\)\.pack'`.idx
+ http_fetch "$R/objects/pack/$name" ".git/objects/pack/$name" &&
+ http_fetch "$R/objects/pack/$idx" ".git/objects/pack/$idx" &&
+ git-verify-pack ".git/objects/pack/$idx" || exit 1
+
+done <"$clone_tmp/packs"
+
+# Then clone refs.
+while read sha1 refname
+do
+ name=`expr "$refname" : 'refs/\(.*\)'` &&
+ git-http-pull -v -a -w "$name" "$name" "$R/" || exit 1
+done <"$clone_tmp/refs"
diff --git a/git-clone-script b/git-clone-script
--- a/git-clone-script
+++ b/git-clone-script
@@ -89,8 +89,17 @@ yes,yes)
rsync $quiet -avz --ignore-existing "$repo/refs/" "$D/.git/refs/"
;;
http://*)
- echo "Somebody should add http fetch" >&2
- exit 1
+ git-clone-dumb-http "$repo" "$D"
+ case "$?" in
+ 2)
+ echo "Somebody should define smarter http server protocol" >&2
+ exit 1
+ ;;
+ 0)
+ ;;
+ *)
+ exit
+ esac
;;
*)
cd "$D" && case "$upload_pack" in
^ permalink raw reply
* Re: [PATCH 1/1] Tell vim the textwidth is 75.
From: Junio C Hamano @ 2005-07-24 1:13 UTC (permalink / raw)
To: Petr Baudis
Cc: Catalin Marinas, Linus Torvalds, git, Bryan larsen, Sam Ravnborg
In-Reply-To: <20050723090433.GA11814@pasky.ji.cz>
Petr Baudis <pasky@suse.cz> writes:
> I have it the other way around, with the rationale that your default
> settings should be in your ~/.gitrc, not environment, which is always
> the highest priority.
That's true. I just never hand commit other people's patches (I
use applymbox for that) and never needed to give one-shot set of
environment variables to commit-tree by hand from the command
line.
> (Quite some things came to git from Cogito anyway. ;-) And well, that's
> completely natural.)
I am not the one who did the barebone, so I'd let Linus to tell
"coming from" and "done independently while retaining
compatibility" apart if he wants to ;-).
>> Personally, I think having to have ignore pattern like .cvsignore
>> per-directory is simply _ugly_.
>
> No, I think it's great. That increases the locality of things, which is
> good. Think about it as of variables - it's nicer to have them local.
Seeing Catalin also expressed the intention to add .gitignore in
directory tree everywhere, I would keep my personal opinion to myself.
How about we do something like this:
git-ls-files --others
--exclude-from=.git/ignore \
--exclude-per-directory=.gitignore
When the new flag --exclude-per-directory is specified,
git-ls-files uses the file with that name in each directory it
looks at to match against the files in that directory (and its
subdirectories, perhaps?) just like it uses --exclude-from for
the entire tree today.
If I added that, would both of you be able to lose a lot of
lines from cg-status and git.__tree_status()? If so, then that
is worth the core-side support.
What should the pattern matching rules be? I think the current
git-ls-files one may be a bit too weak.
^ permalink raw reply
* Re: Last mile to 1.0?
From: Alexey Nezhdanov @ 2005-07-24 3:12 UTC (permalink / raw)
To: git
In-Reply-To: <7vzmsdqwiw.fsf@assigned-by-dhcp.cox.net>
Satturday, 23 July 2005 21:09 Junio C Hamano wrote:
> Instead, please add gitk and gitweb to the list. We should not
> forget that these "mostly read-only" things are Porcelains.
Then add qgit too to provide fair coverage.
--
Respectfully
Alexey Nezhdanov
^ permalink raw reply
* Re: [PATCH] Deb Packaging fixes: Build against Mozilla libs for Debian, conflict with "git"
From: Alexey Nezhdanov @ 2005-07-24 3:28 UTC (permalink / raw)
To: git
In-Reply-To: <20050723192632.GB24071@mythryan2.michonline.com>
Satturday, 23 July 2005 23:26 Ryan Anderson wrote:
> -Depends: ${misc:Depends}, shellutils, diff, rsync, rcs
> +Depends: ${misc:Depends}, patch, diff, rsync, rcs, ssh
Did I missed something or you forgot about libcurl3 dependency?
--
Respectfully
Alexey Nezhdanov
^ permalink raw reply
* Re: [PATCH] Deb Packaging fixes: Build against Mozilla libs for Debian, conflict with "git"
From: Junio C Hamano @ 2005-07-24 6:12 UTC (permalink / raw)
To: Alexey Nezhdanov; +Cc: Ryan Anderson, git
In-Reply-To: <200507240728.40158.snake@penza-gsm.ru>
Alexey Nezhdanov <snake@penza-gsm.ru> writes:
> Satturday, 23 July 2005 23:26 Ryan Anderson wrote:
>> -Depends: ${misc:Depends}, shellutils, diff, rsync, rcs
>> +Depends: ${misc:Depends}, patch, diff, rsync, rcs, ssh
> Did I missed something or you forgot about libcurl3 dependency?
I think you are right. In the build process, dh_shlibdeps is
used and shlib:Depends is created to include the libcurl3 (among
other things) in debian/git-core.substvars, but it is not
actually used in the resulting binary package because the line
misses ${shlibs:Depends} there.
Ryan, would this change be enough? I do not know what I am
doing (${misc:Depends} is new to me), but this patch seems to
fix it on my box.
------------
Make sure binary debian package depends on shlibs it uses.
The "Depends:" line in debian/control lacked ${shlibs:Depends},
which caused the resulting binary package not to depend on
libcurl3 nor even libc6 ;-).
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
# - linus: Fix up applymbox script for the addition of "git-" prefix
# + (working tree)
diff --git a/debian/control b/debian/control
--- a/debian/control
+++ b/debian/control
@@ -7,7 +7,7 @@ Standards-Version: 3.6.1
Package: git-core
Architecture: any
-Depends: ${misc:Depends}, shellutils, diff, rsync, rcs
+Depends: ${shlibs:Depends} ${misc:Depends}, shellutils, diff, rsync, rcs
Description: The git content addressable filesystem
GIT comes in two layers. The bottom layer is merely an extremely fast
and flexible filesystem-based database designed to store directory trees
^ permalink raw reply
* [PATCH] Extend git-ls-files --exclude option to match against directories
From: Marco Costalba @ 2005-07-24 8:12 UTC (permalink / raw)
To: git
Pattern in git-ls-files --exclude=<pattern> can include directories
as example git-ls-files --exclude=Documentation/* will do what you expect
---
ls-files.c | 4 +---
1 files changed, 1 insertions(+), 3 deletions(-)
c8fdfc1f8280a753baf13c293db573c4e50f0a99
diff --git a/ls-files.c b/ls-files.c
--- a/ls-files.c
+++ b/ls-files.c
@@ -80,10 +80,8 @@ static int excluded(const char *pathname
{
int i;
if (nr_excludes) {
- const char *basename = strrchr(pathname, '/');
- basename = (basename) ? basename+1 : pathname;
for (i = 0; i < nr_excludes; i++)
- if (fnmatch(excludes[i], basename, 0) == 0)
+ if (fnmatch(excludes[i], pathname, FNM_PATHNAME) == 0)
return 1;
}
return 0;
__________________________________________________
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around
http://mail.yahoo.com
^ permalink raw reply
* Re: cg-init find error message on empty project directory
From: Petr Baudis @ 2005-07-24 8:46 UTC (permalink / raw)
To: Jerry Seutter; +Cc: Git Mailing List
In-Reply-To: <42D49CEF.9010909@pason.com>
Dear diary, on Wed, Jul 13, 2005 at 06:47:43AM CEST, I got a letter
where Jerry Seutter <jerry.seutter@pason.com> told me that...
> When I run cg-init on an empty directory, it displays the following output:
>
> $ cg-init
> defaulting to local storage area
> find: *: No such file or directory
> Committing initial tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904
> Committed as d25f00309e336884854de8cab8ab9e819294bb83.
>
> Is the find error message supposed to be displayed?
Nope. Thanks for the report, it has been fixed recently.
--
Petr "Pasky" Baudis
Stuff: http://pasky.or.cz/
If you want the holes in your knowledge showing up try teaching
someone. -- Alan Cox
^ permalink raw reply
* Re: Bootstrapping into git, commit gripes at me
From: Petr Baudis @ 2005-07-24 8:57 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Linus Torvalds, Marc Singer, Git Mailing List
In-Reply-To: <7vy88c5r4w.fsf@assigned-by-dhcp.cox.net>
Dear diary, on Tue, Jul 12, 2005 at 11:07:43AM CEST, I got a letter
where Junio C Hamano <junkio@cox.net> told me that...
> If you block certain operations while you have seeked to non-top
> anyway, wouldn't it be cleaner to have .git/seeked-to that
> records the commit ID you are at, which at the same time
> indicates that you are in a special situation, and not touching
> HEAD at all? Then .git/HEAD will always have that line of
> development information.
>
> Well, that was half tongue-in-cheek suggestion; I have a feeling
> that you may feel it is a bit too late to change this kind of
> thing easily.
The thing is, _everything_ assumes .git/HEAD is the current commit
checked out in the index. All the Cogito (that wouldn't be hard to
change at all), all the other Porcelains, the core GIT tools. So
changing that would be difficult and it's much easier this way.
> But if we are going to agree on using .git/head-name, I'd rather
> see it exist all times, so that cat "$GIT_DIR/head-name" would
> always tell us which branch we are working in.
After some thought, I like Linus' approach more now, having head-name
only when it's really necessary.
--
Petr "Pasky" Baudis
Stuff: http://pasky.or.cz/
If you want the holes in your knowledge showing up try teaching
someone. -- Alan Cox
^ permalink raw reply
* [PATCH] Add -a option to get-tag-script to annotate but not sign a tag
From: A Large Angry SCM @ 2005-07-24 15:35 UTC (permalink / raw)
To: Linus Torvalds; +Cc: git
Add -a option to get-tag-script to annotate but not sign a tag
Also fix the test for an existing tag.
Signed-off-by: A Large Angry SCM <gitzilla@gmail.com>
---
git-tag-script | 21 +++++++++++++++------
1 files changed, 15 insertions(+), 6 deletions(-)
a49029e5cf9cca0c2999c0241e75789bcc38dbb5
diff --git a/git-tag-script b/git-tag-script
--- a/git-tag-script
+++ b/git-tag-script
@@ -3,11 +3,15 @@
. git-sh-setup-script || die "Not a git archive"
+annotate=
signed=
force=
while case "$#" in 0) break ;; esac
do
case "$1" in
+ -a)
+ annotate=1
+ ;;
-s)
signed=1
;;
@@ -23,15 +27,16 @@ done
name="$1"
[ "$name" ] || die "I need a tag-name"
-[ -e "$GIT_DIR/refs/tags/$name" ] &&
- [ "$force" ] || die "tag '$name' already exists"
+if [ -e "$GIT_DIR/refs/tags/$name" -a -z "$force" ]; then
+ die "tag '$name' already exists"
+fi
shift
object=$(git-rev-parse --verify --revs-only --default HEAD "$@") || exit 1
type=$(git-cat-file -t $object) || exit 1
tagger=$(git-var GIT_COMMITTER_IDENT) || exit 1
-if [ "$signed" ]; then
+if [ "$annotate" -o "$signed" ]; then
( echo "#"
echo "# Write a tag message"
echo "#" ) > .editmsg
@@ -42,10 +47,14 @@ if [ "$signed" ]; then
[ -s .tagmsg ] || exit
( echo -e "object $object\ntype $type\ntag $name\ntagger $tagger\n"; cat .tagmsg ) > .tmp-tag
- rm -f .tmp-tag.asc .tagmsg
- gpg -bsa .tmp-tag && cat .tmp-tag.asc >> .tmp-tag
+ rm -f .tagmsg
+ if [ "$signed" ]; then
+ rm -f .tmp-tag.asc
+ gpg -bsa .tmp-tag && cat .tmp-tag.asc >> .tmp-tag
+ rm -f .tmp-tag.sig
+ fi
object=$(git-mktag < .tmp-tag)
- rm -f .tmp-tag .tmp-tag.sig
+ rm -f .tmp-tag
fi
mkdir -p "$GIT_DIR/refs/tags"
^ permalink raw reply
* Re: Bootstrapping into git, commit gripes at me
From: Junio C Hamano @ 2005-07-24 16:24 UTC (permalink / raw)
To: Petr Baudis; +Cc: Linus Torvalds, Marc Singer, Git Mailing List
In-Reply-To: <20050724085757.GB7601@pasky.ji.cz>
Petr Baudis <pasky@suse.cz> writes:
> After some thought, I like Linus' approach more now, having head-name
> only when it's really necessary.
I agree 100%. That makes much more sense.
The message from Linus reminded me that the way he tackles a
problem is (as always) simpler, consistent and more elegant than
mine. I have been practicing thinking things through more than
three times before I open my mouth, but still...
^ permalink raw reply
* Re: Should cg-mkpatch output be usable with cg-patch?
From: Wolfgang Denk @ 2005-07-24 17:50 UTC (permalink / raw)
To: Petr Baudis; +Cc: git
In-Reply-To: <20050722210352.GG11916@pasky.ji.cz>
In message <20050722210352.GG11916@pasky.ji.cz> you wrote:
>
> > I wander what I should do with "cg-mkpatch" generated output; I had
> > the impression that this should be usable with "cg-patch", but these
> > are incompatible with each other.
>
> They certainly aren't.
Is this a problem with the current implementation, or intentional?
> > diff --git a/tools/img2brec.sh b/tools/img2brec.sh
> > old mode 100644
> > new mode 100755
> > ...
> > If I feed this into "cg-patch", I get:
> > patch: **** Only garbage was found in the patch input.
>
> And did the changes apply? :-)
No, they did not. No file modes were changed.
> The message is surely confusing, but appeared to be rather corner-casy
> and after I imagined the required complexity of filtering this, I
> decided to spend my time on something more useful for the time being. :)
I don't think this is a corner case. I think it is pretty important
to be able to promote permissiong changes.
Best regards,
Wolfgang Denk
--
Software Engineering: Embedded and Realtime Systems, Embedded Linux
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de
Too many people are ready to carry the stool when the piano needs to
be moved.
^ permalink raw reply
* Problems Importing a CVS tree into FAUmachine
From: Thomas Glanzmann @ 2005-07-24 20:56 UTC (permalink / raw)
To: Matthias Urlichs, GIT
Hello Matthias,
I have problems importing the FAUmachine CVS tree into git. I tried the
following with git HEAD and cvsps 2.1:
git-cvsimport-script -d /var/lib/cvsd/cvsroots -C /var/lib/cvsd/gitroots/FAUmachine FAUmachine
<Output of rlog and 'treated as before'>
Committing initial tree e30105bb454c40a143689b37c11340f1a8f084b4
Unknown: error
If I symlink HEAD afterwards manual to .git/refs/heads/origin I can see that
every until April 28 is imported (which is a very big amount). If I try
the above statement for the mutt cvs repository, it works perfectly for
me.
Any help is highly appreciated.
To make it easy reproducable I put the tar.gz on my webpage. But beware
of the size (202 Mbyte)!
http://wwwcip.informatik.uni-erlangen.de/~sithglan/FAUmachine.tgz (202M)
Thanks,
Thomas
^ permalink raw reply
* Re: Should cg-mkpatch output be usable with cg-patch?
From: Petr Baudis @ 2005-07-24 22:48 UTC (permalink / raw)
To: Wolfgang Denk; +Cc: git
In-Reply-To: <20050724175018.3D721352B73@atlas.denx.de>
Dear diary, on Sun, Jul 24, 2005 at 07:50:18PM CEST, I got a letter
where Wolfgang Denk <wd@denx.de> told me that...
> In message <20050722210352.GG11916@pasky.ji.cz> you wrote:
> >
> > > I wander what I should do with "cg-mkpatch" generated output; I had
> > > the impression that this should be usable with "cg-patch", but these
> > > are incompatible with each other.
> >
> > They certainly aren't.
>
> Is this a problem with the current implementation, or intentional?
Implementation. Should be fixed now, after rewriting part of it. ;-)
> > The message is surely confusing, but appeared to be rather corner-casy
> > and after I imagined the required complexity of filtering this, I
> > decided to spend my time on something more useful for the time being. :)
>
> I don't think this is a corner case. I think it is pretty important
> to be able to promote permissiong changes.
Of course. By the corner case I meant "the external patch tool invoked
by cg-patch emits bogus error in case of patch containing only mode
changes and nothing else". I wasn't aware that the changes wouldn't
apply at all - actually, I was when I was writing the code, but
completely forgot about it later.
Thanks,
--
Petr "Pasky" Baudis
Stuff: http://pasky.or.cz/
If you want the holes in your knowledge showing up try teaching
someone. -- Alan Cox
^ permalink raw reply
* [RFC] extending git-ls-files --exclude.
From: Junio C Hamano @ 2005-07-24 22:49 UTC (permalink / raw)
To: Petr Baudis, Catalin Marinas, Linus Torvalds; +Cc: git, Marco Costalba
In-Reply-To: <20050722205948.GE11916@pasky.ji.cz>
Petr Baudis <pasky@suse.cz> writes:
> Yes. There were several discussions about this in the past, with no
> clear outcome, IIRC. I would prefer:
>
> ~/.git/ignore per-user
> /.git/ignore per-repository
> .gitignore per-directory (cummulative with parent directories)
>
> Note that I also want to make use of some special characters in this
> file ... to make it at least as powerful as CVS' ignore.
I'd like to extend "--exclude" and friends git-ls-files takes
the following way (strawman). I'd appreciate your input from
the perspective of Porcelain writers, and somebody who ends up
having to use the bare Plumbing.
I'll be sending patches for actual implementation in separate messages.
------------
'git-ls-files' can use a list of "exclude patterns" when
traversing the directory tree and finding files to show when the
flags --others or --ignored are specified.
These exclude patterns come from these places:
(1) command line flag --exclude=<pattern> specifies a single
pattern.
(2) command line flag --exclude-from=<file> specifies a list of
patterns stored in a file.
(3) command line flag --exclude-per-directory=<name> specifies
a name of the file in each directory 'git-ls-files'
examines, and if exists, its contents are used as an
additional list of patterns.
An exclude pattern file used by (2) and (3) contains one pattern
per line. A line that starts with a '#' can be used as comment
for readability.
The list of patterns that is in effect at a given time is
built and ordered in the following way:
* --exclude=<pattern> and lines read from --exclude-from=<file>
come at the beginning of the list of patterns, in the order
given on the command line. Patterns that come from the file
specified with --exclude-from are ordered in the same order
as they appear in the file.
* When --exclude-per-directory=<name> is specified, upon
entering a directory that has such a file, its contents are
appended at the end of the current "list of patterns". They
are popped off when leaving the directory.
Each pattern in the pattern list specifies "a match pattern" and
optionally the fate --- either a file that matches the pattern
is considered excluded or included. By default, this being
"exclude" mechanism, the fate is "excluded". A filename is
examined against the patterns in the list, and the first match
determines its fate.
A pattern specified on the command line with --exclude or read
from the file specified with --exclude-from is relative to the
top of the directory tree. A pattern read from a file specified
by --exclude-per-directory is relative to the directory that the
pattern file appears in.
An exclude pattern is of the following format:
- an optional prefix '!' which means that the fate this pattern
specifies is "include", not the usual "exclude"; the
remainder of the pattern string is interpreted according to
the following rules.
- if it does not contain a slash '/', it is a shell glob
pattern and used to match against the filename without
leading directories (i.e. the same way as the current
implementation).
- otherwise, it is a shell glob pattern, suitable for
consumption by fnmatch(3) with FNM_PATHNAME flag. I.e. a
slash in the pattern must match a slash in the pathname.
"Documentation/*.html" matches "Documentation/git.html" but
not "ppc/ppc.html". As a natural exception, "/*.c" matches
"cat-file.c" but not "mozilla-sha1/sha1.c".
An example:
$ cat .git/ignore
# ignore objects and archives, anywhere in the tree.
*.[oa]
$ cat Documentation/.gitignore
# ignore generated html files,
# except foo.html which is maintained by hand
!foo.html
*.html
$ git-ls-files --ignored \
--exclude='Documentation/*.[0-9]' \
--exclude-from=.git/ignore \
--exclude-per-directory=.gitignore
^ permalink raw reply
* [PATCH] git-ls-files: --exclude mechanism updates.
From: Junio C Hamano @ 2005-07-24 22:50 UTC (permalink / raw)
To: Linus Torvalds; +Cc: git
In-Reply-To: <7vd5p73jlu.fsf@assigned-by-dhcp.cox.net>
Add --exclude-per-directory=<name> option that specifies a file
to contain exclude patterns local to that directory and its
subdirectories. Update the exclusion logic to be able to say
"include files that match this more specific pattern, even
though later exclude patterns may match them". Also enhances
that a pattern can contain '/' in which case fnmatch is called
with FNM_PATHNAME flag to match the entire path.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
ls-files.c | 123 ++++++++++++++++++++++++++++++------
t/t3001-ls-files-others-exclude.sh | 55 ++++++++++++++++
2 files changed, 157 insertions(+), 21 deletions(-)
create mode 100755 t/t3001-ls-files-others-exclude.sh
d1466fd8701ca79a91b41c6225c115a0a9866d6e
diff --git a/ls-files.c b/ls-files.c
--- a/ls-files.c
+++ b/ls-files.c
@@ -25,20 +25,31 @@ static const char *tag_removed = "";
static const char *tag_other = "";
static const char *tag_killed = "";
+static char *exclude_per_dir = NULL;
static int nr_excludes;
-static const char **excludes;
static int excludes_alloc;
+static struct exclude {
+ const char *pattern;
+ const char *base;
+ int baselen;
+} **excludes;
-static void add_exclude(const char *string)
+static void add_exclude(const char *string, const char *base, int baselen)
{
+ struct exclude *x = xmalloc(sizeof (*x));
+
+ x->pattern = string;
+ x->base = base;
+ x->baselen = baselen;
if (nr_excludes == excludes_alloc) {
excludes_alloc = alloc_nr(excludes_alloc);
excludes = realloc(excludes, excludes_alloc*sizeof(char *));
}
- excludes[nr_excludes++] = string;
+ excludes[nr_excludes++] = x;
}
-static void add_excludes_from_file(const char *fname)
+static int add_excludes_from_file_1(const char *fname,
+ const char *base, int baselen)
{
int fd, i;
long size;
@@ -53,7 +64,7 @@ static void add_excludes_from_file(const
lseek(fd, 0, SEEK_SET);
if (size == 0) {
close(fd);
- return;
+ return 0;
}
buf = xmalloc(size);
if (read(fd, buf, size) != size)
@@ -63,28 +74,89 @@ static void add_excludes_from_file(const
entry = buf;
for (i = 0; i < size; i++) {
if (buf[i] == '\n') {
- if (entry != buf + i) {
+ if (entry != buf + i && entry[0] != '#') {
buf[i] = 0;
- add_exclude(entry);
+ add_exclude(entry, base, baselen);
}
entry = buf + i + 1;
}
}
- return;
+ return 0;
-err: perror(fname);
- exit(1);
+ 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) < 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];
+ int current_nr = nr_excludes;
+
+ 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);
+ }
+ return current_nr;
+}
+
+static void pop_exclude_per_directory(int stk)
+{
+ while (stk < nr_excludes)
+ free(excludes[--nr_excludes]);
}
static int excluded(const char *pathname)
{
int i;
+
if (nr_excludes) {
- const char *basename = strrchr(pathname, '/');
- basename = (basename) ? basename+1 : pathname;
- for (i = 0; i < nr_excludes; i++)
- if (fnmatch(excludes[i], basename, 0) == 0)
- return 1;
+ int pathlen = strlen(pathname);
+
+ for (i = 0; i < nr_excludes; i++) {
+ struct exclude *x = 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) inplicitly
+ * 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 0;
}
@@ -121,7 +193,7 @@ static void add_name(const char *pathnam
* doesn't handle them at all yet. Maybe that will change some
* day.
*
- * Also, we currently ignore all names starting with a dot.
+ * Also, we ignore the name ".git" (even if it is not a directory).
* That likely will not change.
*/
static void read_directory(const char *path, const char *base, int baselen)
@@ -129,10 +201,13 @@ static void read_directory(const char *p
DIR *dir = opendir(path);
if (dir) {
+ 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(dir)) != NULL) {
int len;
@@ -141,10 +216,10 @@ static void read_directory(const char *p
!strcmp(de->d_name + 1, ".") ||
!strcmp(de->d_name + 1, "git")))
continue;
- if (excluded(de->d_name) != show_ignored)
- continue;
len = strlen(de->d_name);
memcpy(fullname + baselen, de->d_name, len+1);
+ if (excluded(fullname) != show_ignored)
+ continue;
switch (DTYPE(de)) {
struct stat st;
@@ -170,6 +245,8 @@ static void read_directory(const char *p
add_name(fullname, baselen + len);
}
closedir(dir);
+
+ pop_exclude_per_directory(exclude_stk);
}
}
@@ -287,7 +364,9 @@ static void show_files(void)
static const char *ls_files_usage =
"git-ls-files [-z] [-t] (--[cached|deleted|others|stage|unmerged|killed])* "
- "[ --ignored [--exclude=<pattern>] [--exclude-from=<file>) ]";
+ "[ --ignored ] [--exclude=<pattern>] [--exclude-from=<file>] "
+ "[ --exclude-per-directory=<filename> ]";
+;
int main(int argc, char **argv)
{
@@ -323,13 +402,15 @@ int main(int argc, char **argv)
show_stage = 1;
show_unmerged = 1;
} else if (!strcmp(arg, "-x") && i+1 < argc) {
- add_exclude(argv[++i]);
+ add_exclude(argv[++i], "", 0);
} else if (!strncmp(arg, "--exclude=", 10)) {
- add_exclude(arg+10);
+ add_exclude(arg+10, "", 0);
} else if (!strcmp(arg, "-X") && i+1 < argc) {
add_excludes_from_file(argv[++i]);
} else if (!strncmp(arg, "--exclude-from=", 15)) {
add_excludes_from_file(arg+15);
+ } else if (!strncmp(arg, "--exclude-per-directory=", 24)) {
+ exclude_per_dir = arg + 24;
} else
usage(ls_files_usage);
}
diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh
new file mode 100755
--- /dev/null
+++ b/t/t3001-ls-files-others-exclude.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git-ls-files --others --exclude
+
+This test runs git-ls-files --others and tests --exclude patterns.
+'
+
+. ./test-lib.sh
+
+rm -fr one three
+for dir in . one one/two three
+do
+ mkdir -p $dir &&
+ for i in 1 2 3 4 5
+ do
+ >$dir/a.$i
+ done
+done
+
+cat >expect <<EOF
+a.2
+a.4
+a.5
+one/a.3
+one/a.4
+one/a.5
+one/two/a.3
+one/two/a.5
+three/a.2
+three/a.3
+three/a.4
+three/a.5
+EOF
+
+echo '.gitignore
+output
+expect
+.gitignore
+' >.git/ignore
+
+echo '*.1
+/*.3' >.gitignore
+echo '*.2
+two/*.4' >one/.gitignore
+
+test_expect_success \
+ 'git-ls-files --others --exclude.' \
+ 'git-ls-files --others \
+ --exclude-per-directory=.gitignore \
+ --exclude-from=.git/ignore \
+ >output &&
+ diff -u expect output'
^ permalink raw reply
* [PATCH] Documentation: describe git-ls-files --exclude patterns.
From: Junio C Hamano @ 2005-07-24 22:51 UTC (permalink / raw)
To: Linus Torvalds; +Cc: git
In-Reply-To: <7vd5p73jlu.fsf@assigned-by-dhcp.cox.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
Documentation/git-ls-files.txt | 96 ++++++++++++++++++++++++++++++++++++++--
1 files changed, 92 insertions(+), 4 deletions(-)
d9296497b70d9007da94cec453ecb5c6c7173140
diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt
--- a/Documentation/git-ls-files.txt
+++ b/Documentation/git-ls-files.txt
@@ -14,6 +14,7 @@ SYNOPSIS
(-[c|d|o|i|s|u|k])\*
[-x <pattern>|--exclude=<pattern>]
[-X <file>|--exclude-from=<file>]
+ [--exclude-per-directory=<file>]
DESCRIPTION
-----------
@@ -59,10 +60,10 @@ OPTIONS
-X|--exclude-from=<file>::
exclude patterns are read from <file>; 1 per line.
- Allows the use of the famous dontdiff file as follows to find
- out about uncommitted files just as dontdiff is used with
- the diff command:
- git-ls-files --others --exclude-from=dontdiff
+
+--exclude-per-directory=<file>::
+ read additional exclude patterns that apply only to the
+ directory and its subdirectories in <file>.
-t::
Identify the file status with the following tags (followed by
@@ -89,6 +90,93 @@ the dircache records up to three such pa
the user (or Cogito) to see what should eventually be recorded at the
path. (see read-cache for more information on state)
+
+Exclude Patterns
+----------------
+
+'git-ls-files' can use a list of "exclude patterns" when
+traversing the directory tree and finding files to show when the
+flags --others or --ignored are specified.
+
+These exclude patterns come from these places:
+
+ (1) command line flag --exclude=<pattern> specifies a single
+ pattern.
+
+ (2) command line flag --exclude-from=<file> specifies a list of
+ patterns stored in a file.
+
+ (3) command line flag --exclude-per-directory=<name> specifies
+ a name of the file in each directory 'git-ls-files'
+ examines, and if exists, its contents are used as an
+ additional list of patterns.
+
+An exclude pattern file used by (2) and (3) contains one pattern
+per line. A line that starts with a '#' can be used as comment
+for readability.
+
+The list of patterns that is in effect at a given time is
+built and ordered in the following way:
+
+ * --exclude=<pattern> and lines read from --exclude-from=<file>
+ come at the beginning of the list of patterns, in the order
+ given on the command line. Patterns that come from the file
+ specified with --exclude-from are ordered in the same order
+ as they appear in the file.
+
+ * When --exclude-per-directory=<name> is specified, upon
+ entering a directory that has such a file, its contents are
+ appended at the end of the current "list of patterns". They
+ are popped off when leaving the directory.
+
+Each pattern in the pattern list specifies "a match pattern" and
+optionally the fate --- either a file that matches the pattern
+is considered excluded or included. By default, this being
+"exclude" mechanism, the fate is "excluded". A filename is
+examined against the patterns in the list, and the first match
+determines its fate.
+
+A pattern specified on the command line with --exclude or read
+from the file specified with --exclude-from is relative to the
+top of the directory tree. A pattern read from a file specified
+by --exclude-per-directory is relative to the directory that the
+pattern file appears in.
+
+An exclude pattern is of the following format:
+
+ - an optional prefix '!' which means that the fate this pattern
+ specifies is "include", not the usual "exclude"; the
+ remainder of the pattern string is interpreted according to
+ the following rules.
+
+ - if it does not contain a slash '/', it is a shell glob
+ pattern and used to match against the filename without
+ leading directories (i.e. the same way as the current
+ implementation).
+
+ - otherwise, it is a shell glob pattern, suitable for
+ consumption by fnmatch(3) with FNM_PATHNAME flag. I.e. a
+ slash in the pattern must match a slash in the pathname.
+ "Documentation/*.html" matches "Documentation/git.html" but
+ not "ppc/ppc.html". As a natural exception, "/*.c" matches
+ "cat-file.c" but not "mozilla-sha1/sha1.c".
+
+An example:
+
+ $ cat .git/ignore
+ # ignore objects and archives, anywhere in the tree.
+ *.[oa]
+ $ cat Documentation/.gitignore
+ # ignore generated html files,
+ # except foo.html which is maintained by hand
+ !foo.html
+ *.html
+ $ git-ls-files --ignored \
+ --exclude='Documentation/*.[0-9]' \
+ --exclude-from=.git/ignore \
+ --exclude-per-directory=.gitignore
+
+
See Also
--------
link:read-cache.html[read-cache]
^ permalink raw reply
* Re: [PATCH] Deb Packaging fixes: Build against Mozilla libs for Debian, conflict with "git"
From: Ryan Anderson @ 2005-07-25 4:27 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Alexey Nezhdanov, git
In-Reply-To: <7vwtngg2bd.fsf@assigned-by-dhcp.cox.net>
On Sat, Jul 23, 2005 at 11:12:22PM -0700, Junio C Hamano wrote:
> Alexey Nezhdanov <snake@penza-gsm.ru> writes:
>
> > Satturday, 23 July 2005 23:26 Ryan Anderson wrote:
> >> -Depends: ${misc:Depends}, shellutils, diff, rsync, rcs
> >> +Depends: ${misc:Depends}, patch, diff, rsync, rcs, ssh
> > Did I missed something or you forgot about libcurl3 dependency?
>
> I think you are right. In the build process, dh_shlibdeps is
> used and shlib:Depends is created to include the libcurl3 (among
> other things) in debian/git-core.substvars, but it is not
> actually used in the resulting binary package because the line
> misses ${shlibs:Depends} there.
>
> Ryan, would this change be enough? I do not know what I am
> doing (${misc:Depends} is new to me), but this patch seems to
> fix it on my box.
Yes - this looks like the right idea - but you're missing a comma
between the two variables. (That will cause the *entire* depends line
to get dropped, btw.)
I changed it from shlibs:Depends to misc:Depends because I saw something
when I was tracking down exactly what I needed to put into debian/compat
(in fact, man debhelper, then search for "compat" and look under V4),
but I misread "supplements" to be something else.
So, Acked-By: me, if you put the comma in. :)
> ------------
> Make sure binary debian package depends on shlibs it uses.
>
> The "Depends:" line in debian/control lacked ${shlibs:Depends},
> which caused the resulting binary package not to depend on
> libcurl3 nor even libc6 ;-).
>
> Signed-off-by: Junio C Hamano <junkio@cox.net>
> ---
>
> # - linus: Fix up applymbox script for the addition of "git-" prefix
> # + (working tree)
> diff --git a/debian/control b/debian/control
> --- a/debian/control
> +++ b/debian/control
> @@ -7,7 +7,7 @@ Standards-Version: 3.6.1
>
> Package: git-core
> Architecture: any
> -Depends: ${misc:Depends}, shellutils, diff, rsync, rcs
> +Depends: ${shlibs:Depends} ${misc:Depends}, shellutils, diff, rsync, rcs
> Description: The git content addressable filesystem
> GIT comes in two layers. The bottom layer is merely an extremely fast
> and flexible filesystem-based database designed to store directory trees
>
>
>
> -
> To unsubscribe from this list: send the line "unsubscribe git" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
--
Ryan Anderson
sometimes Pug Majere
^ permalink raw reply
* [PATCH] Add support for directories to git-rename-script.
From: Ryan Anderson @ 2005-07-25 5:26 UTC (permalink / raw)
To: Linus Torvalds; +Cc: git
Oh, and in the process, rewrite it in Perl.
Signed-off-by: Ryan Anderson <ryan@michonline.com>
---
git-rename-script | 68 +++++++++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 63 insertions(+), 5 deletions(-)
f46717f9116bba7efb6a10ed99cd2fcea00fe5da
diff --git a/git-rename-script b/git-rename-script
--- a/git-rename-script
+++ b/git-rename-script
@@ -1,7 +1,65 @@
-#!/bin/sh
+#!/usr/bin/perl
+#
+# Copyright 2005, Ryan Anderson <ryan@michonline.com>
+#
+# This file is licensed under the GPL v2, or a later version
+# at the discretion of Linus Torvalds.
-. git-sh-setup-script || die "Not a git archive"
-[ -f "$1" ] || [ -h "$1" ] || die "git rename: bad source"
-[ -e "$2" ] && die "git rename: destination already exists"
-mv -- "$1" "$2" && git-update-cache --add --remove -- "$1" "$2"
+use warnings;
+use strict;
+
+sub usage($);
+
+# Sanity checks:
+unless ( -d ".git" && -d ".git/objects" &&
+ -d ".git/objects/00" && -d ".git/refs") {
+ usage("Git repository not found.");
+}
+
+usage("") if scalar @ARGV != 2;
+
+my ($src,$dst) = @ARGV;
+
+unless (-f $src || -l $src || -d $src) {
+ usage("git rename: bad source '$src'");
+}
+
+if (-e $dst) {
+ usage("git rename: destinations '$dst' already exists");
+}
+
+my (@allfiles,@srcfiles,@dstfiles);
+
+open(F,"-|","git-ls-files")
+ or die "Failed to open pipe from git-ls-files: " . $!;
+
+@allfiles = <F>;
+close(F);
+chomp for @allfiles;
+
+
+@srcfiles = grep /^$src/, @allfiles;
+@dstfiles = @srcfiles;
+s#^$src(/|$)#$dst$1# for @dstfiles;
+
+rename($src,$dst)
+ or die "rename failed: $!";
+
+system("git-update-cache","--remove","--",@srcfiles);
+system("git-update-cache","--add","--",@dstfiles);
+
+
+sub usage($) {
+ my $s = shift;
+ print $s, "\n" if (length $s != 0);
+ print <<EOT;
+$0 <source> <dest>
+source must exist and be either a file, symlink or directory.
+dest must NOT exist.
+
+Renames source to dest, and updates the git cache to reflect the change.
+Use "git commit" to make record the change permanently.
+EOT
+ exit(1);
+}
--
Ryan Anderson
sometimes Pug Majere
^ permalink raw reply
* Re: [PATCH] Add git-find-new-files to spot files added to the tree, but not the repository
From: Ryan Anderson @ 2005-07-25 6:00 UTC (permalink / raw)
To: Linus Torvalds; +Cc: git
In-Reply-To: <Pine.LNX.4.58.0507231043330.6074@g5.osdl.org>
On Sat, Jul 23, 2005 at 10:48:39AM -0700, Linus Torvalds wrote:
>
> Oh, and btw, maybe you didn't realize that "git-ls-files --others" already
> basically does what your script does? Without any filtering - it was meant
> to be run from a script, so something like
That's exactly what I was missing.
The usage on a lot of the commands is very, well, "terse".
I totally missed "others" and I'm not sure I would have caught the
significance right away, anyway.
I was going to start a patch series to pull the man pages into each
program - but then I just decided it'd be easier to install the man
pages.
--
Ryan Anderson
sometimes Pug Majere
^ permalink raw reply
* [PATCH] Add documentation for git-rename-script
From: Ryan Anderson @ 2005-07-25 6:13 UTC (permalink / raw)
To: Linus Torvalds; +Cc: git
In-Reply-To: <20050725052646.GB6098@mythryan2.michonline.com>
Signed-off-by: Ryan Anderson <ryan@michonline.com>
---
Documentation/git-rename-script.txt | 34 ++++++++++++++++++++++++++++++++++
1 files changed, 34 insertions(+), 0 deletions(-)
create mode 100644 Documentation/git-rename-script.txt
715924203401309ceb2f696e507b8b2c18244d08
diff --git a/Documentation/git-rename-script.txt b/Documentation/git-rename-script.txt
new file mode 100644
--- /dev/null
+++ b/Documentation/git-rename-script.txt
@@ -0,0 +1,34 @@
+
+git-rename-script(1)
+=====================
+v0.1, May 2005
+
+NAME
+----
+git-rename-script - Script used to rename a file, directory or symlink.
+
+
+SYNOPSIS
+--------
+'git-rename-script' <source> <destination>
+
+DESCRIPTION
+-----------
+This script is used to rename a file, directory or symlink.
+
+The index is updated after successful completion, but the change must still be
+committed.
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+Rewritten by Ryan Anderson <ryan@michonline.com>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
^ permalink raw reply
* [PATCH] Update the documentation for git-tag-script to reflect current behavior.
From: Ryan Anderson @ 2005-07-25 6:17 UTC (permalink / raw)
To: Linus Torvalds; +Cc: git
Signed-off-by: Ryan Anderson <ryan@michonline.com>
---
Documentation/git-tag-script.txt | 12 ++++++++----
1 files changed, 8 insertions(+), 4 deletions(-)
8f1801c948cbf1187a192df1656709689ba46d61
diff --git a/Documentation/git-tag-script.txt b/Documentation/git-tag-script.txt
--- a/Documentation/git-tag-script.txt
+++ b/Documentation/git-tag-script.txt
@@ -4,18 +4,22 @@ v0.1, May 2005
NAME
----
-git-tag-script - An example script to create a tag object signed with GPG
+git-tag-script - Create a tag object signed with GPG
SYNOPSIS
--------
-'git-tag-script'
+'git-tag-script' [-s] [-f] <name>
DESCRIPTION
-----------
-This is an example script that uses "git-mktag" to create a tag object
-signed with GPG.
+Adds a "tag" reference in .git/refs/tags/
+
+Unless "-f" is given, the tag must not yet exist in ".git/refs/tags"
+
+If "-s" is passed, the user will be prompted for a tag message, and a GnuPG
+signed tag object will be created as well.
Author
--
Ryan Anderson
sometimes Pug Majere
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox