On Wed, Nov 27, 2013 at 7:00 PM, Thomas Gummerer <[email protected]> wrote:
> This makes update-index use both partial reading and partial writing.
> Partial reading is only used no option other than the paths is passed to
> the command.
>
> This passes the test suite,
Just checking, the test suite was run with TEST_GIT_INDEX_VERSION=5, right?
> but doesn't behave correctly when a write
> fails. A log should be written to the lock file, in order to be able to
> recover if a write fails.
>From the API point of view this looks nice (you should have hidden
needs_write = 1 in cache_invalidate_path and change_cache_version
though) We could support partial file removal too by marking removed
files "removed", but that impacts the reading code and may have bad
interaction with cache_invalidate_path/needs_rewrite. Probably not
worth the effort until someone shows us they remove stuff often.
> ---
> builtin/update-index.c | 43 +++++++++++---
> cache-tree.c | 13 +++++
> cache-tree.h | 1 +
> cache.h | 27 ++++++++-
> lockfile.c | 2 +-
> read-cache-v2.c | 2 +
> read-cache-v5.c | 154
> ++++++++++++++++++++++++++++++++++++++++---------
> read-cache.c | 30 ++++++++++
> read-cache.h | 1 +
> resolve-undo.c | 1 +
> 10 files changed, 237 insertions(+), 37 deletions(-)
>
> diff --git a/builtin/update-index.c b/builtin/update-index.c
> index 8b3f7a0..69f0949 100644
> --- a/builtin/update-index.c
> +++ b/builtin/update-index.c
> @@ -56,6 +56,7 @@ static int mark_ce_flags(const char *path, int flag, int
> mark)
> else
> active_cache[pos]->ce_flags &= ~flag;
> cache_tree_invalidate_path(active_cache_tree, path);
> + the_index.needs_rewrite = 1;
> active_cache_changed = 1;
> return 0;
> }
> @@ -99,6 +100,8 @@ static int add_one_path(const struct cache_entry *old,
> const char *path, int len
> memcpy(ce->name, path, len);
> ce->ce_flags = create_ce_flags(0);
> ce->ce_namelen = len;
> + if (old)
> + ce->entry_pos = old->entry_pos;
> fill_stat_cache_info(ce, st);
> ce->ce_mode = ce_mode_from_stat(old, st->st_mode);
>
> @@ -268,6 +271,7 @@ static void chmod_path(int flip, const char *path)
> goto fail;
> }
> cache_tree_invalidate_path(active_cache_tree, path);
> + the_index.needs_rewrite = 1;
> active_cache_changed = 1;
> report("chmod %cx '%s'", flip, path);
> return;
> @@ -706,15 +710,18 @@ static int reupdate_callback(struct parse_opt_ctx_t
> *ctx,
>
> int cmd_update_index(int argc, const char **argv, const char *prefix)
> {
> - int newfd, entries, has_errors = 0, line_termination = '\n';
> + int newfd, has_errors = 0, line_termination = '\n';
> int read_from_stdin = 0;
> int prefix_length = prefix ? strlen(prefix) : 0;
> int preferred_index_format = 0;
> char set_executable_bit = 0;
> struct refresh_params refresh_args = {0, &has_errors};
> int lock_error = 0;
> + struct filter_opts opts;
> + struct pathspec pathspec;
> struct lock_file *lock_file;
> struct parse_opt_ctx_t ctx;
> + int i, needs_full_read = 0;
> int parseopt_state = PARSE_OPT_UNKNOWN;
> struct option options[] = {
> OPT_BIT('q', NULL, &refresh_args.flags,
> @@ -810,9 +817,23 @@ int cmd_update_index(int argc, const char **argv, const
> char *prefix)
> if (newfd < 0)
> lock_error = errno;
>
> - entries = read_cache();
> - if (entries < 0)
> - die("cache corrupted");
> + for (i = 0; i < argc; i++) {
> + if (!prefixcmp(argv[i], "--"))
> + needs_full_read = 1;
> + }
> + if (!needs_full_read) {
> + memset(&opts, 0, sizeof(struct filter_opts));
> + parse_pathspec(&pathspec, 0,
> + PATHSPEC_PREFER_CWD |
> + PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,
> + prefix, argv + 1);
> + opts.pathspec = &pathspec;
> + if (read_cache_filtered(&opts) < 0)
> + die("cache corrupted");
> + } else {
> + if (read_cache() < 0)
> + die("cache corrupted");
> + }
>
> /*
> * Custom copy of parse_options() because we want to handle
> @@ -862,6 +883,7 @@ int cmd_update_index(int argc, const char **argv, const
> char *prefix)
> preferred_index_format,
> INDEX_FORMAT_LB, INDEX_FORMAT_UB);
>
> + the_index.needs_rewrite = 1;
> active_cache_changed = 1;
> change_cache_version(preferred_index_format);
> }
> @@ -890,17 +912,22 @@ int cmd_update_index(int argc, const char **argv, const
> char *prefix)
> }
>
> if (active_cache_changed) {
> + int r;
> if (newfd < 0) {
> if (refresh_args.flags & REFRESH_QUIET)
> exit(128);
> unable_to_lock_index_die(get_index_file(),
> lock_error);
> }
> - if (write_cache(newfd, active_cache, active_nr) ||
> - commit_locked_index(lock_file))
> + r = write_cache_partial(newfd);
> + if (r < 0)
> die("Unable to write new index file");
> + else if (r == 0)
> + commit_lock_file(lock_file);
> + else
> + remove_lock_file();
> + } else {
> + rollback_lock_file(lock_file);
> }
>
> - rollback_lock_file(lock_file);
> -
> return has_errors ? 1 : 0;
> }
> diff --git a/cache-tree.c b/cache-tree.c
> index 1209732..a3d18bb 100644
> --- a/cache-tree.c
> +++ b/cache-tree.c
> @@ -123,6 +123,15 @@ void cache_tree_invalidate_path(struct cache_tree *it,
> const char *path)
> return;
> slash = strchr(path, '/');
> it->entry_count = -1;
> + /*
> + * Mark the cache_tree directory entry as invalid too. The
> + * entry_count defines if the tree is valid, so we don't need
> + * to reset any other field.
> + */
> + if (it->de_ref) {
> + it->de_ref->de_nentries = -1;
> + it->de_ref->changed = 1;
> + }
> if (!slash) {
> int pos;
> namelen = strlen(path);
> @@ -140,6 +149,10 @@ void cache_tree_invalidate_path(struct cache_tree *it,
> const char *path)
> sizeof(struct cache_tree_sub *) *
> (it->subtree_nr - pos - 1));
> it->subtree_nr--;
> + if (it->de_ref) {
> + it->de_ref->de_nsubtrees--;
> + it->de_ref->changed = 1;
> + }
> }
> return;
> }
> diff --git a/cache-tree.h b/cache-tree.h
> index 9818926..eaf14a9 100644
> --- a/cache-tree.h
> +++ b/cache-tree.h
> @@ -18,6 +18,7 @@ struct cache_tree {
> unsigned char sha1[20];
> int subtree_nr;
> int subtree_alloc;
> + struct directory_entry *de_ref;
> struct cache_tree_sub **down;
> };
>
> diff --git a/cache.h b/cache.h
> index 71b98cf..1a634dc 100644
> --- a/cache.h
> +++ b/cache.h
> @@ -137,11 +137,31 @@ struct cache_entry {
> unsigned int ce_namelen;
> unsigned char sha1[20];
> uint32_t ce_stat_crc;
> + unsigned int entry_pos;
> + unsigned int changed;
> struct cache_entry *next; /* used by name_hash */
> struct cache_entry *next_ce;
> char name[FLEX_ARRAY]; /* more */
> };
>
> +struct directory_entry {
> + struct directory_entry **sub;
> + struct directory_entry *next;
> + struct directory_entry *next_hash;
> + struct cache_entry *ce;
> + struct cache_entry *ce_last;
> + uint32_t de_foffset;
> + uint32_t de_nsubtrees;
> + uint32_t de_nfiles;
> + uint32_t de_nentries;
> + unsigned char sha1[20];
> + uint16_t de_flags;
> + uint32_t de_pathlen;
> + uint32_t entry_pos;
> + unsigned int changed;
> + char pathname[FLEX_ARRAY];
> +};
> +
> #define CE_NAMEMASK (0x0fff)
> #define CE_STAGEMASK (0x3000)
> #define CE_EXTENDED (0x4000)
> @@ -317,13 +337,15 @@ struct filter_opts {
>
> struct index_state {
> struct cache_entry **cache;
> + struct directory_entry *root_directory;
> unsigned int version;
> unsigned int cache_nr, cache_alloc, cache_changed;
> struct string_list *resolve_undo;
> struct cache_tree *cache_tree;
> struct cache_time timestamp;
> unsigned name_hash_initialized : 1,
> - initialized : 1;
> + initialized : 1,
> + needs_rewrite : 1;
> struct hash_table name_hash;
> struct hash_table dir_hash;
> struct index_ops *ops;
> @@ -353,6 +375,7 @@ extern void free_name_hash(struct index_state *istate);
> #define is_cache_unborn() is_index_unborn(&the_index)
> #define read_cache_unmerged() read_index_unmerged(&the_index)
> #define write_cache(newfd, cache, entries) write_index(&the_index, (newfd))
> +#define write_cache_partial(newfd) write_index_partial(&the_index, (newfd))
> #define discard_cache() discard_index(&the_index)
> #define unmerged_cache() unmerged_index(&the_index)
> #define cache_name_pos(name, namelen)
> index_name_pos(&the_index,(name),(namelen))
> @@ -529,6 +552,7 @@ extern int read_index_from(struct index_state *, const
> char *path);
> extern int is_index_unborn(struct index_state *);
> extern int read_index_unmerged(struct index_state *);
> extern int write_index(struct index_state *, int newfd);
> +extern int write_index_partial(struct index_state *, int newfd);
> extern int discard_index(struct index_state *);
> extern int unmerged_index(const struct index_state *);
> extern int verify_path(const char *path);
> @@ -613,6 +637,7 @@ extern NORETURN void unable_to_lock_index_die(const char
> *path, int err);
> extern int hold_lock_file_for_update(struct lock_file *, const char *path,
> int);
> extern int hold_lock_file_for_append(struct lock_file *, const char *path,
> int);
> extern int commit_lock_file(struct lock_file *);
> +extern void remove_lock_file(void);
> extern void update_index_if_able(struct index_state *, struct lock_file *);
>
> extern int hold_locked_index(struct lock_file *, int);
> diff --git a/lockfile.c b/lockfile.c
> index 8fbcb6a..c150e5c 100644
> --- a/lockfile.c
> +++ b/lockfile.c
> @@ -7,7 +7,7 @@
> static struct lock_file *lock_file_list;
> static const char *alternate_index_output;
>
> -static void remove_lock_file(void)
> +void remove_lock_file(void)
> {
> pid_t me = getpid();
>
> diff --git a/read-cache-v2.c b/read-cache-v2.c
> index f884c10..1fec892 100644
> --- a/read-cache-v2.c
> +++ b/read-cache-v2.c
> @@ -555,5 +555,7 @@ struct index_ops v2_ops = {
> match_stat_basic,
> verify_hdr,
> read_index_v2,
> + write_index_v2,
> + /* Partial writing is the same as writing the full index for v2 */
> write_index_v2
> };
> diff --git a/read-cache-v5.c b/read-cache-v5.c
> index a5e9b5a..13436a3 100644
> --- a/read-cache-v5.c
> +++ b/read-cache-v5.c
> @@ -20,22 +20,6 @@ struct extension_header {
> uint32_t crc;
> };
>
> -struct directory_entry {
> - struct directory_entry **sub;
> - struct directory_entry *next;
> - struct directory_entry *next_hash;
> - struct cache_entry *ce;
> - struct cache_entry *ce_last;
> - uint32_t de_foffset;
> - uint32_t de_nsubtrees;
> - uint32_t de_nfiles;
> - uint32_t de_nentries;
> - unsigned char sha1[20];
> - uint16_t de_flags;
> - uint32_t de_pathlen;
> - char pathname[FLEX_ARRAY];
> -};
> -
> struct conflict_part {
> struct conflict_part *next;
> uint16_t flags;
> @@ -246,7 +230,7 @@ static struct directory_entry *read_directories(unsigned
> int *dir_offset,
> offsetof(struct ondisk_directory_entry, name) - 5;
> disk_de = ptr_add(mmap, *dir_offset);
> de = directory_entry_from_ondisk(disk_de, len);
> -
> + de->entry_pos = *dir_offset;
> data_len = len + 1 + offsetof(struct ondisk_directory_entry, name);
> filecrc = ptr_add(mmap, *dir_offset + data_len);
> if (!check_crc32(0, ptr_add(mmap, *dir_offset), data_len,
> ntoh_l(*filecrc)))
> @@ -281,6 +265,7 @@ static int read_entry(struct cache_entry **ce, char
> *pathname, size_t pathlen,
> entry_offset = first_entry_offset + ntoh_l(*beginning);
> disk_ce = ptr_add(mmap, entry_offset);
> *ce = cache_entry_from_ondisk(disk_ce, pathname, len, pathlen);
> + (*ce)->entry_pos = entry_offset;
> filecrc = ptr_add(mmap, entry_offset + len + 1 + sizeof(*disk_ce));
> if (!check_crc32(0,
> ptr_add(mmap, entry_offset), len + 1 + sizeof(*disk_ce),
> @@ -439,6 +424,7 @@ static struct cache_tree *convert_one(struct
> directory_entry *de)
>
> it = cache_tree();
> it->entry_count = de->de_nentries;
> + it->de_ref = de;
> if (0 <= it->entry_count)
> hashcpy(it->sha1, de->sha1);
>
> @@ -523,14 +509,6 @@ static int read_entries(struct index_state *istate,
> struct directory_entry *de,
> return 0;
> }
>
> -static void free_directory_tree(struct directory_entry *de) {
> - int i;
> -
> - for (i = 0; i < de->de_pathlen; i++)
> - free_directory_tree(de->sub[i]);
> - free(de);
> -}
> -
> /*
> * Read an index-v5 file filtered by the filter_opts. If opts is NULL,
> * everything will be read.
> @@ -626,7 +604,7 @@ static int read_index_v5(struct index_state *istate, void
> *mmap,
> }
> }
> istate->cache_tree = cache_tree_convert_v5(root_directory);
> - free_directory_tree(root_directory);
> + istate->root_directory = root_directory;
> istate->cache_nr = nr;
> return 0;
> }
> @@ -696,6 +674,7 @@ static void ce_smudge_racily_clean_entry(struct
> cache_entry *ce)
> * that hasn't changed checking the sha1.
> */
> ce->ce_flags |= CE_SMUDGED;
> + ce->changed = 1;
> }
>
> static char *super_directory(char *filename)
> @@ -1231,6 +1210,103 @@ static int write_resolve_undo(struct index_state
> *istate,
> return 0;
> }
>
> +static int write_ce_if_necessary(struct cache_entry *ce, void *cb_data)
> +{
> + int *fdx = cb_data, pathlen, size;
> + int fd = *fdx;
> + char *dir;
> + struct ondisk_cache_entry *ondisk;
> + uint32_t crc;
> +
> + assert(ce->entry_pos != 0);
> + /* TODO I'm just using the_index out of lazyness here */
> + if (!ce_uptodate(ce) && is_racy_timestamp(&the_index, ce))
> + ce_smudge_racily_clean_entry(ce);
> + if (!ce->changed)
> + return 0;
> + if (is_null_sha1(ce->sha1)) {
> + static const char msg[] = "cache entry has null sha1: %s";
> + static int allow = -1;
> +
> + if (allow < 0)
> + allow = git_env_bool("GIT_ALLOW_NULL_SHA1", 0);
> + if (allow)
> + warning(msg, ce->name);
> + else
> + return error(msg, ce->name);
> + }
> + dir = super_directory(ce->name);
> + pathlen = dir ? strlen(dir) + 1 : 0;
> + size = offsetof(struct ondisk_cache_entry, name) +
> + ce_namelen(ce) - pathlen + 1;
> + ondisk = xmalloc(size);
> +
> + crc = 0;
> + ondisk_from_cache_entry(ce, ondisk, pathlen);
> + if (lseek(fd, ce->entry_pos, SEEK_SET) < ce->entry_pos)
> + die("eror ce seeking");
> + if (ce_write(&crc, fd, ondisk, size) < 0)
> + return -1;
> + crc = htonl(crc);
> + if (ce_write(NULL, fd, &crc, 4) < 0)
> + return -1;
> + return ce_flush(fd);
> +}
> +
> +static void ondisk_from_directory_entry_partial(struct directory_entry *de,
> + struct ondisk_directory_entry
> *ondisk)
> +{
> + ondisk->foffset = htonl(de->de_foffset);
> + ondisk->nsubtrees = htonl(de->de_nsubtrees);
> + ondisk->nfiles = htonl(de->de_nfiles);
> + ondisk->nentries = htonl(de->de_nentries);
> + hashcpy(ondisk->sha1, de->sha1);
> + ondisk->flags = htons(de->de_flags);
> + if (de->de_pathlen == 0) {
> + memcpy(ondisk->name, "\0", 1);
> + } else {
> + memcpy(ondisk->name, de->pathname, de->de_pathlen);
> + memcpy(ondisk->name + de->de_pathlen - 1, "/\0", 2);
> + }
> +}
> +
> +static int write_directories_partial(struct directory_entry *de, int fd)
> +{
> + int ondisk_size = offsetof(struct ondisk_directory_entry, name);
> + int size = ondisk_size + de->de_pathlen + 1;
> + int i;
> + uint32_t crc;
> + struct ondisk_directory_entry *ondisk;
> +
> + if (de->changed) {
> + crc = 0;
> + ondisk = xmalloc(size);
> + ondisk_from_directory_entry_partial(de, ondisk);
> + if (lseek(fd, de->entry_pos, SEEK_SET) < de->entry_pos)
> + die("error directory seeking");;
> + if (ce_write(&crc, fd, ondisk, size) < 0)
> + return -1;
> + crc = htonl(crc);
> + if (ce_write(NULL, fd, &crc, 4) < 0)
> + return -1;
> + free(ondisk);
> + if (ce_flush(fd) < 0)
> + return -1;
> + }
> + for (i = 0; i < de->de_nsubtrees; i++) {
> + if (write_directories_partial(de->sub[i], fd) < 0)
> + return -1;
> + }
> + return 0;
> +}
> +
> +static int write_partial(struct index_state *istate, int fd)
> +{
> + write_directories_partial(istate->root_directory, fd);
> +
> + return for_each_index_entry(istate, write_ce_if_necessary, &fd);
> +}
> +
> static int write_index_v5(struct index_state *istate, int newfd)
> {
> struct cache_header hdr;
> @@ -1296,9 +1372,33 @@ static int write_index_v5(struct index_state *istate,
> int newfd)
> return ce_flush(newfd);
> }
>
> +static int write_index_partial_v5(struct index_state *istate, int newfd)
> +{
> + int fd;
> + char *path = get_index_file();
> +
> + if (istate->needs_rewrite || istate->cache_nr == 0)
> + return write_index_v5(istate, newfd);
> + if (istate->filter_opts && istate->needs_rewrite)
> + die("BUG: cannot write a partially read index");
> + fd = open(path, O_RDWR, 0666);
> + if (fd < 0) {
> + if (errno == ENOENT)
> + die("no index file exists cannot do a partial write");
> + die_errno("index file opening for writing failed");
> + }
> +
> + if (write_partial(istate, fd) < 0)
> + return -1;
> + if (ce_flush(fd) < 0)
> + return -1;
> + return 1;
> +}
> +
> struct index_ops v5_ops = {
> match_stat_basic,
> verify_hdr,
> read_index_v5,
> - write_index_v5
> + write_index_v5,
> + write_index_partial_v5
> };
> diff --git a/read-cache.c b/read-cache.c
> index 04430e5..1cad0e2 100644
> --- a/read-cache.c
> +++ b/read-cache.c
> @@ -32,6 +32,9 @@ static void replace_index_entry(struct index_state *istate,
> int nr, struct cache
>
> remove_name_hash(istate, old);
> set_index_entry(istate, nr, ce);
> + ce->changed = 1;
> + if (ce->entry_pos == 0)
> + istate->needs_rewrite = 1;
> istate->cache_changed = 1;
> }
>
> @@ -494,6 +497,7 @@ int remove_index_entry_at(struct index_state *istate, int
> pos)
>
> record_resolve_undo(istate, ce);
> remove_name_hash(istate, ce);
> + istate->needs_rewrite = 1;
> istate->cache_changed = 1;
> istate->cache_nr--;
> if (pos >= istate->cache_nr)
> @@ -520,6 +524,7 @@ void remove_marked_cache_entries(struct index_state
> *istate)
> else
> ce_array[j++] = ce_array[i];
> }
> + istate->needs_rewrite = 1;
> istate->cache_changed = 1;
> istate->cache_nr = j;
> }
> @@ -1024,6 +1029,7 @@ int add_index_entry(struct index_state *istate, struct
> cache_entry *ce, int opti
> istate->cache + pos,
> (istate->cache_nr - pos - 1) * sizeof(ce));
> set_index_entry(istate, pos, ce);
> + istate->needs_rewrite = 1;
> istate->cache_changed = 1;
> return 0;
> }
> @@ -1108,6 +1114,8 @@ static struct cache_entry *refresh_cache_ent(struct
> index_state *istate,
> size = ce_size(ce);
> updated = xmalloc(size);
> memcpy(updated, ce, size);
> + updated->changed = 1;
> + updated->entry_pos = ce->entry_pos;
> fill_stat_cache_info(updated, &st);
> /*
> * If ignore_valid is not set, we should leave CE_VALID bit
> @@ -1201,6 +1209,8 @@ int refresh_index(struct index_state *istate, unsigned
> int flags,
> * means the index is not valid anymore.
> */
> ce->ce_flags &= ~CE_VALID;
> + /* TODO: remove this maybe? */
> + istate->needs_rewrite = 1;
> istate->cache_changed = 1;
> }
> if (quiet)
> @@ -1241,6 +1251,8 @@ void initialize_index(struct index_state *istate, int
> version)
> version = atoi(envversion);
> }
> istate->version = version;
> + istate->needs_rewrite = 0;
> + istate->root_directory = NULL;
> set_istate_ops(istate);
> }
>
> @@ -1427,6 +1439,16 @@ int is_index_unborn(struct index_state *istate)
> return (!istate->cache_nr && !istate->timestamp.sec);
> }
>
> +static void free_directory_tree(struct directory_entry *de) {
> + int i;
> +
> + if (!de)
> + return;
> + for (i = 0; i < de->de_pathlen; i++)
> + free_directory_tree(de->sub[i]);
> + free(de);
> +}
> +
> int discard_index(struct index_state *istate)
> {
> int i;
> @@ -1435,6 +1457,7 @@ int discard_index(struct index_state *istate)
> free(istate->cache[i]);
> resolve_undo_clear_index(istate);
> istate->cache_nr = 0;
> + istate->needs_rewrite = 0;
> istate->cache_changed = 0;
> istate->timestamp.sec = 0;
> istate->timestamp.nsec = 0;
> @@ -1446,6 +1469,8 @@ int discard_index(struct index_state *istate)
> istate->cache_alloc = 0;
> istate->ops = NULL;
> istate->filter_opts = NULL;
> + free_directory_tree(istate->root_directory);
> + istate->root_directory = NULL;
> return 0;
> }
>
> @@ -1491,6 +1516,11 @@ int write_index(struct index_state *istate, int newfd)
> return istate->ops->write_index(istate, newfd);
> }
>
> +int write_index_partial(struct index_state *istate, int newfd)
> +{
> + return istate->ops->write_index_partial(istate, newfd);
> +}
> +
> /*
> * Read the index file that is potentially unmerged into given
> * index_state, dropping any unmerged entries. Returns true if
> diff --git a/read-cache.h b/read-cache.h
> index 9d66df6..e7f36ae 100644
> --- a/read-cache.h
> +++ b/read-cache.h
> @@ -31,6 +31,7 @@ struct index_ops {
> int (*read_index)(struct index_state *istate, void *mmap, unsigned
> long mmap_size,
> struct filter_opts *opts);
> int (*write_index)(struct index_state *istate, int newfd);
> + int (*write_index_partial)(struct index_state *istate, int newfd);
> };
>
> extern struct index_ops v2_ops;
> diff --git a/resolve-undo.c b/resolve-undo.c
> index c09b006..c496c20 100644
> --- a/resolve-undo.c
> +++ b/resolve-undo.c
> @@ -110,6 +110,7 @@ void resolve_undo_clear_index(struct index_state *istate)
> string_list_clear(resolve_undo, 1);
> free(resolve_undo);
> istate->resolve_undo = NULL;
> + istate->needs_rewrite = 1;
> istate->cache_changed = 1;
> }
>
> --
> 1.8.4.2
>
--
Duy
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html