From 60b458b7d31ff2497ed90cbe9f65444d84882cec Mon Sep 17 00:00:00 2001 From: Kjetil Barvik Date: Mon, 9 Feb 2009 21:54:04 +0100 Subject: [PATCH 001/289] lstat_cache(): small cleanup and optimisation Simplify the if-else test in longest_match_lstat_cache() such that we only have one simple if test. Instead of testing for 'i == cache.len' or 'i == len', we transform this to a common test for 'i == max_len'. And to further optimise we use 'i >= max_len' instead of 'i == max_len', the reason is that it is now the exact opposite of one part inside the while-loop termination expression 'i < max_len && name[i] == cache.path[i]', and then the compiler can probably reuse a test instruction from it. We also throw away the arguments to reset_lstat_cache(), such that all the safeguard logic inside lstat_cache() is handled at one place. Signed-off-by: Kjetil Barvik Signed-off-by: Junio C Hamano --- symlinks.c | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/symlinks.c b/symlinks.c index f262b7c44b..ae57e5603b 100644 --- a/symlinks.c +++ b/symlinks.c @@ -25,27 +25,30 @@ static inline int longest_match_lstat_cache(int len, const char *name, } i++; } - /* Is the cached path string a substring of 'name'? */ - if (i == cache.len && cache.len < len && name[cache.len] == '/') { + /* + * Is the cached path string a substring of 'name', is 'name' + * a substring of the cached path string, or is 'name' and the + * cached path string the exact same string? + */ + if (i >= max_len && ((len > cache.len && name[cache.len] == '/') || + (len < cache.len && cache.path[len] == '/') || + (len == cache.len))) { match_len_prev = match_len; - match_len = cache.len; - /* Is 'name' a substring of the cached path string? */ - } else if ((i == len && len < cache.len && cache.path[len] == '/') || - (i == len && len == cache.len)) { - match_len_prev = match_len; - match_len = len; + match_len = i; } *previous_slash = match_len_prev; return match_len; } -static inline void reset_lstat_cache(int track_flags, int prefix_len_stat_func) +static inline void reset_lstat_cache(void) { cache.path[0] = '\0'; cache.len = 0; cache.flags = 0; - cache.track_flags = track_flags; - cache.prefix_len_stat_func = prefix_len_stat_func; + /* + * The track_flags and prefix_len_stat_func members is only + * set by the safeguard rule inside lstat_cache() + */ } #define FL_DIR (1 << 0) @@ -77,11 +80,13 @@ static int lstat_cache(int len, const char *name, if (cache.track_flags != track_flags || cache.prefix_len_stat_func != prefix_len_stat_func) { /* - * As a safeguard we clear the cache if the values of - * track_flags and/or prefix_len_stat_func does not - * match with the last supplied values. + * As a safeguard rule we clear the cache if the + * values of track_flags and/or prefix_len_stat_func + * does not match with the last supplied values. */ - reset_lstat_cache(track_flags, prefix_len_stat_func); + reset_lstat_cache(); + cache.track_flags = track_flags; + cache.prefix_len_stat_func = prefix_len_stat_func; match_len = last_slash = 0; } else { /* @@ -153,7 +158,7 @@ static int lstat_cache(int len, const char *name, cache.path[last_slash] = '\0'; cache.len = last_slash; cache.flags = save_flags; - } else if (track_flags & FL_DIR && + } else if ((track_flags & FL_DIR) && last_slash_dir > 0 && last_slash_dir <= PATH_MAX) { /* * We have a separate test for the directory case, @@ -170,7 +175,7 @@ static int lstat_cache(int len, const char *name, cache.len = last_slash_dir; cache.flags = FL_DIR; } else { - reset_lstat_cache(track_flags, prefix_len_stat_func); + reset_lstat_cache(); } return ret_flags; } @@ -190,8 +195,7 @@ void invalidate_lstat_cache(int len, const char *name) cache.len = previous_slash; cache.flags = FL_DIR; } else - reset_lstat_cache(cache.track_flags, - cache.prefix_len_stat_func); + reset_lstat_cache(); } } @@ -200,7 +204,7 @@ void invalidate_lstat_cache(int len, const char *name) */ void clear_lstat_cache(void) { - reset_lstat_cache(0, 0); + reset_lstat_cache(); } #define USE_ONLY_LSTAT 0 From 148bc06b9101042342a89454a003998fc0e1ded3 Mon Sep 17 00:00:00 2001 From: Kjetil Barvik Date: Mon, 9 Feb 2009 21:54:05 +0100 Subject: [PATCH 002/289] lstat_cache(): generalise longest_match_lstat_cache() Rename the function to longst_path_match() and generalise it such that it can also be used by other functions. Signed-off-by: Kjetil Barvik Signed-off-by: Junio C Hamano --- symlinks.c | 70 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/symlinks.c b/symlinks.c index ae57e5603b..4596aee9dc 100644 --- a/symlinks.c +++ b/symlinks.c @@ -1,5 +1,37 @@ #include "cache.h" +/* + * Returns the length (on a path component basis) of the longest + * common prefix match of 'name_a' and 'name_b'. + */ +static int longest_path_match(const char *name_a, int len_a, + const char *name_b, int len_b, + int *previous_slash) +{ + int max_len, match_len = 0, match_len_prev = 0, i = 0; + + max_len = len_a < len_b ? len_a : len_b; + while (i < max_len && name_a[i] == name_b[i]) { + if (name_a[i] == '/') { + match_len_prev = match_len; + match_len = i; + } + i++; + } + /* + * Is 'name_b' a substring of 'name_a', the other way around, + * or is 'name_a' and 'name_b' the exact same string? + */ + if (i >= max_len && ((len_a > len_b && name_a[len_b] == '/') || + (len_a < len_b && name_b[len_a] == '/') || + (len_a == len_b))) { + match_len_prev = match_len; + match_len = i; + } + *previous_slash = match_len_prev; + return match_len; +} + static struct cache_def { char path[PATH_MAX + 1]; int len; @@ -8,38 +40,6 @@ static struct cache_def { int prefix_len_stat_func; } cache; -/* - * Returns the length (on a path component basis) of the longest - * common prefix match of 'name' and the cached path string. - */ -static inline int longest_match_lstat_cache(int len, const char *name, - int *previous_slash) -{ - int max_len, match_len = 0, match_len_prev = 0, i = 0; - - max_len = len < cache.len ? len : cache.len; - while (i < max_len && name[i] == cache.path[i]) { - if (name[i] == '/') { - match_len_prev = match_len; - match_len = i; - } - i++; - } - /* - * Is the cached path string a substring of 'name', is 'name' - * a substring of the cached path string, or is 'name' and the - * cached path string the exact same string? - */ - if (i >= max_len && ((len > cache.len && name[cache.len] == '/') || - (len < cache.len && cache.path[len] == '/') || - (len == cache.len))) { - match_len_prev = match_len; - match_len = i; - } - *previous_slash = match_len_prev; - return match_len; -} - static inline void reset_lstat_cache(void) { cache.path[0] = '\0'; @@ -94,7 +94,8 @@ static int lstat_cache(int len, const char *name, * the 2 "excluding" path types. */ match_len = last_slash = - longest_match_lstat_cache(len, name, &previous_slash); + longest_path_match(name, len, cache.path, cache.len, + &previous_slash); match_flags = cache.flags & track_flags & (FL_NOENT|FL_SYMLINK); if (match_flags && match_len == cache.len) return match_flags; @@ -188,7 +189,8 @@ void invalidate_lstat_cache(int len, const char *name) { int match_len, previous_slash; - match_len = longest_match_lstat_cache(len, name, &previous_slash); + match_len = longest_path_match(name, len, cache.path, cache.len, + &previous_slash); if (len == match_len) { if ((cache.track_flags & FL_DIR) && previous_slash > 0) { cache.path[previous_slash] = '\0'; From 571998921d8fd4ee674545406aabb86987921252 Mon Sep 17 00:00:00 2001 From: Kjetil Barvik Date: Mon, 9 Feb 2009 21:54:06 +0100 Subject: [PATCH 003/289] lstat_cache(): swap func(length, string) into func(string, length) Swap function argument pair (length, string) into (string, length) to conform with the commonly used order inside the GIT source code. Also, add a note about this fact into the coding guidelines. Signed-off-by: Kjetil Barvik Signed-off-by: Junio C Hamano --- Documentation/CodingGuidelines | 3 +++ builtin-add.c | 2 +- builtin-apply.c | 2 +- builtin-update-index.c | 2 +- cache.h | 8 ++++---- diff-lib.c | 2 +- dir.c | 2 +- entry.c | 2 +- symlinks.c | 16 ++++++++-------- unpack-trees.c | 4 ++-- 10 files changed, 23 insertions(+), 20 deletions(-) diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines index 0d7fa9cca9..b8bf618a30 100644 --- a/Documentation/CodingGuidelines +++ b/Documentation/CodingGuidelines @@ -129,3 +129,6 @@ For C programs: used in the git core command set (unless your command is clearly separate from it, such as an importer to convert random-scm-X repositories to git). + + - When we pass pair to functions, we should try to + pass them in that order. diff --git a/builtin-add.c b/builtin-add.c index ac98c8354d..a23ad96773 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -148,7 +148,7 @@ static const char **validate_pathspec(int argc, const char **argv, const char *p if (pathspec) { const char **p; for (p = pathspec; *p; p++) { - if (has_symlink_leading_path(strlen(*p), *p)) { + if (has_symlink_leading_path(*p, strlen(*p))) { int len = prefix ? strlen(prefix) : 0; die("'%s' is beyond a symbolic link", *p + len); } diff --git a/builtin-apply.c b/builtin-apply.c index f312798af3..106be94105 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -2360,7 +2360,7 @@ static int check_to_create_blob(const char *new_name, int ok_if_exists) * In such a case, path "new_name" does not exist as * far as git is concerned. */ - if (has_symlink_leading_path(strlen(new_name), new_name)) + if (has_symlink_leading_path(new_name, strlen(new_name))) return 0; return error("%s: already exists in working directory", new_name); diff --git a/builtin-update-index.c b/builtin-update-index.c index 5604977505..6c55527513 100644 --- a/builtin-update-index.c +++ b/builtin-update-index.c @@ -195,7 +195,7 @@ static int process_path(const char *path) struct stat st; len = strlen(path); - if (has_symlink_leading_path(len, path)) + if (has_symlink_leading_path(path, len)) return error("'%s' is beyond a symbolic link", path); /* diff --git a/cache.h b/cache.h index 2d889deb26..80eeeb7db8 100644 --- a/cache.h +++ b/cache.h @@ -724,10 +724,10 @@ struct checkout { }; extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath); -extern int has_symlink_leading_path(int len, const char *name); -extern int has_symlink_or_noent_leading_path(int len, const char *name); -extern int has_dirs_only_path(int len, const char *name, int prefix_len); -extern void invalidate_lstat_cache(int len, const char *name); +extern int has_symlink_leading_path(const char *name, int len); +extern int has_symlink_or_noent_leading_path(const char *name, int len); +extern int has_dirs_only_path(const char *name, int len, int prefix_len); +extern void invalidate_lstat_cache(const char *name, int len); extern void clear_lstat_cache(void); extern struct alternate_object_database { diff --git a/diff-lib.c b/diff-lib.c index a41e1ec07c..a3ba20ee29 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -31,7 +31,7 @@ static int check_removed(const struct cache_entry *ce, struct stat *st) return -1; return 1; } - if (has_symlink_leading_path(ce_namelen(ce), ce->name)) + if (has_symlink_leading_path(ce->name, ce_namelen(ce))) return 1; if (S_ISDIR(st->st_mode)) { unsigned char sub[20]; diff --git a/dir.c b/dir.c index cfd1ea587d..8fb5226542 100644 --- a/dir.c +++ b/dir.c @@ -720,7 +720,7 @@ int read_directory(struct dir_struct *dir, const char *path, const char *base, i { struct path_simplify *simplify; - if (has_symlink_leading_path(strlen(path), path)) + if (has_symlink_leading_path(path, strlen(path))) return dir->nr; simplify = create_simplify(pathspec); diff --git a/entry.c b/entry.c index 05aa58d348..bb6bdb90e3 100644 --- a/entry.c +++ b/entry.c @@ -20,7 +20,7 @@ static void create_directories(const char *path, const struct checkout *state) * we test the path components of the prefix with the * stat() function instead of the lstat() function. */ - if (has_dirs_only_path(len, buf, state->base_dir_len)) + if (has_dirs_only_path(buf, len, state->base_dir_len)) continue; /* ok, it is already a directory. */ /* diff --git a/symlinks.c b/symlinks.c index 4596aee9dc..51672868d1 100644 --- a/symlinks.c +++ b/symlinks.c @@ -70,7 +70,7 @@ static inline void reset_lstat_cache(void) * of the prefix, where the cache should use the stat() function * instead of the lstat() function to test each path component. */ -static int lstat_cache(int len, const char *name, +static int lstat_cache(const char *name, int len, int track_flags, int prefix_len_stat_func) { int match_len, last_slash, last_slash_dir, previous_slash; @@ -185,7 +185,7 @@ static int lstat_cache(int len, const char *name, * Invalidate the given 'name' from the cache, if 'name' matches * completely with the cache. */ -void invalidate_lstat_cache(int len, const char *name) +void invalidate_lstat_cache(const char *name, int len) { int match_len, previous_slash; @@ -214,9 +214,9 @@ void clear_lstat_cache(void) /* * Return non-zero if path 'name' has a leading symlink component */ -int has_symlink_leading_path(int len, const char *name) +int has_symlink_leading_path(const char *name, int len) { - return lstat_cache(len, name, + return lstat_cache(name, len, FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) & FL_SYMLINK; } @@ -225,9 +225,9 @@ int has_symlink_leading_path(int len, const char *name) * Return non-zero if path 'name' has a leading symlink component or * if some leading path component does not exists. */ -int has_symlink_or_noent_leading_path(int len, const char *name) +int has_symlink_or_noent_leading_path(const char *name, int len) { - return lstat_cache(len, name, + return lstat_cache(name, len, FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT) & (FL_SYMLINK|FL_NOENT); } @@ -239,9 +239,9 @@ int has_symlink_or_noent_leading_path(int len, const char *name) * 'prefix_len', thus we then allow for symlinks in the prefix part as * long as those points to real existing directories. */ -int has_dirs_only_path(int len, const char *name, int prefix_len) +int has_dirs_only_path(const char *name, int len, int prefix_len) { - return lstat_cache(len, name, + return lstat_cache(name, len, FL_DIR|FL_FULLPATH, prefix_len) & FL_DIR; } diff --git a/unpack-trees.c b/unpack-trees.c index e547282ed5..2293158850 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -61,7 +61,7 @@ static void unlink_entry(struct cache_entry *ce) char *cp, *prev; char *name = ce->name; - if (has_symlink_or_noent_leading_path(ce_namelen(ce), ce->name)) + if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce))) return; if (unlink(name)) return; @@ -583,7 +583,7 @@ static int verify_absent(struct cache_entry *ce, const char *action, if (o->index_only || o->reset || !o->update) return 0; - if (has_symlink_or_noent_leading_path(ce_namelen(ce), ce->name)) + if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce))) return 0; if (!lstat(ce->name, &st)) { From 7847892716a3c9a7b8facc076fc056ac425bcfe6 Mon Sep 17 00:00:00 2001 From: Kjetil Barvik Date: Mon, 9 Feb 2009 21:54:07 +0100 Subject: [PATCH 004/289] unlink_entry(): introduce schedule_dir_for_removal() Currently inside unlink_entry() if we get a successful removal of one file with unlink(), we try to remove the leading directories each and every time. So if one directory containing 200 files is moved to an other location we get 199 failed calls to rmdir() and 1 successful call. To fix this and avoid some unnecessary calls to rmdir(), we schedule each directory for removal and wait much longer before we do the real call to rmdir(). Since the unlink_entry() function is called with alphabetically sorted names, this new function end up being very effective to avoid unnecessary calls to rmdir(). In some cases over 95% of all calls to rmdir() is removed with this patch. Signed-off-by: Kjetil Barvik Signed-off-by: Junio C Hamano --- cache.h | 2 ++ symlinks.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++ unpack-trees.c | 30 +++++-------------------- 3 files changed, 67 insertions(+), 24 deletions(-) diff --git a/cache.h b/cache.h index 80eeeb7db8..1bf2d4bde6 100644 --- a/cache.h +++ b/cache.h @@ -729,6 +729,8 @@ extern int has_symlink_or_noent_leading_path(const char *name, int len); extern int has_dirs_only_path(const char *name, int len, int prefix_len); extern void invalidate_lstat_cache(const char *name, int len); extern void clear_lstat_cache(void); +extern void schedule_dir_for_removal(const char *name, int len); +extern void remove_scheduled_dirs(void); extern struct alternate_object_database { struct alternate_object_database *next; diff --git a/symlinks.c b/symlinks.c index 51672868d1..1d6b35b858 100644 --- a/symlinks.c +++ b/symlinks.c @@ -245,3 +245,62 @@ int has_dirs_only_path(const char *name, int len, int prefix_len) FL_DIR|FL_FULLPATH, prefix_len) & FL_DIR; } + +static struct removal_def { + char path[PATH_MAX]; + int len; +} removal; + +static void do_remove_scheduled_dirs(int new_len) +{ + while (removal.len > new_len) { + removal.path[removal.len] = '\0'; + if (rmdir(removal.path)) + break; + do { + removal.len--; + } while (removal.len > new_len && + removal.path[removal.len] != '/'); + } + removal.len = new_len; + return; +} + +void schedule_dir_for_removal(const char *name, int len) +{ + int match_len, last_slash, i, previous_slash; + + match_len = last_slash = i = + longest_path_match(name, len, removal.path, removal.len, + &previous_slash); + /* Find last slash inside 'name' */ + while (i < len) { + if (name[i] == '/') + last_slash = i; + i++; + } + + /* + * If we are about to go down the directory tree, we check if + * we must first go upwards the tree, such that we then can + * remove possible empty directories as we go upwards. + */ + if (match_len < last_slash && match_len < removal.len) + do_remove_scheduled_dirs(match_len); + /* + * If we go deeper down the directory tree, we only need to + * save the new path components as we go down. + */ + if (match_len < last_slash) { + memcpy(&removal.path[match_len], &name[match_len], + last_slash - match_len); + removal.len = last_slash; + } + return; +} + +void remove_scheduled_dirs(void) +{ + do_remove_scheduled_dirs(0); + return; +} diff --git a/unpack-trees.c b/unpack-trees.c index 2293158850..e3c3fa12aa 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -52,36 +52,17 @@ static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce, add_index_entry(&o->result, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|ADD_CACHE_SKIP_DFCHECK); } -/* Unlink the last component and attempt to remove leading - * directories, in case this unlink is the removal of the - * last entry in the directory -- empty directories are removed. +/* + * Unlink the last component and schedule the leading directories for + * removal, such that empty directories get removed. */ static void unlink_entry(struct cache_entry *ce) { - char *cp, *prev; - char *name = ce->name; - if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce))) return; - if (unlink(name)) + if (unlink(ce->name)) return; - prev = NULL; - while (1) { - int status; - cp = strrchr(name, '/'); - if (prev) - *prev = '/'; - if (!cp) - break; - - *cp = 0; - status = rmdir(name); - if (status) { - *cp = '/'; - break; - } - prev = cp; - } + schedule_dir_for_removal(ce->name, ce_namelen(ce)); } static struct checkout state; @@ -117,6 +98,7 @@ static int check_updates(struct unpack_trees_options *o) continue; } } + remove_scheduled_dirs(); for (i = 0; i < index->cache_nr; i++) { struct cache_entry *ce = index->cache[i]; From 81a9aa60a193f1181149b69920930e15c4e34059 Mon Sep 17 00:00:00 2001 From: Kjetil Barvik Date: Mon, 9 Feb 2009 21:54:08 +0100 Subject: [PATCH 005/289] create_directories(): remove some memcpy() and strchr() calls Remove the call to memcpy() and strchr() for each path component tested, and instead add each path component as we go forward inside the while-loop. Impact: small optimisation Signed-off-by: Kjetil Barvik Signed-off-by: Junio C Hamano --- entry.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/entry.c b/entry.c index bb6bdb90e3..cc8f0c6077 100644 --- a/entry.c +++ b/entry.c @@ -2,15 +2,19 @@ #include "blob.h" #include "dir.h" -static void create_directories(const char *path, const struct checkout *state) +static void create_directories(const char *path, int path_len, + const struct checkout *state) { - int len = strlen(path); - char *buf = xmalloc(len + 1); - const char *slash = path; + char *buf = xmalloc(path_len + 1); + int len = 0; - while ((slash = strchr(slash+1, '/')) != NULL) { - len = slash - path; - memcpy(buf, path, len); + while (len < path_len) { + do { + buf[len] = path[len]; + len++; + } while (len < path_len && path[len] != '/'); + if (len >= path_len) + break; buf[len] = 0; /* @@ -190,6 +194,7 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t memcpy(path, state->base_dir, len); strcpy(path + len, ce->name); + len += ce_namelen(ce); if (!lstat(path, &st)) { unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID); @@ -218,6 +223,6 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t return error("unable to unlink old '%s' (%s)", path, strerror(errno)); } else if (state->not_new) return 0; - create_directories(path, state); + create_directories(path, len, state); return write_entry(ce, path, state, 0); } From 4857c761e35b07c12ff2ef1140e93b071b8ac4e7 Mon Sep 17 00:00:00 2001 From: Kjetil Barvik Date: Mon, 9 Feb 2009 21:54:50 +0100 Subject: [PATCH 006/289] write_entry(): cleanup of some duplicated code The switch-cases for S_IFREG and S_IFLNK was so similar that it will be better to do some cleanup and use the common parts of it. And the entry.c file should now be clean for 'gcc -Wextra' warnings. Signed-off-by: Kjetil Barvik Signed-off-by: Junio C Hamano --- entry.c | 73 +++++++++++++++++++++++---------------------------------- 1 file changed, 29 insertions(+), 44 deletions(-) diff --git a/entry.c b/entry.c index cc8f0c6077..1f53588a11 100644 --- a/entry.c +++ b/entry.c @@ -78,7 +78,7 @@ static int create_file(const char *path, unsigned int mode) return open(path, O_WRONLY | O_CREAT | O_EXCL, mode); } -static void *read_blob_entry(struct cache_entry *ce, const char *path, unsigned long *size) +static void *read_blob_entry(struct cache_entry *ce, unsigned long *size) { enum object_type type; void *new = read_sha1_file(ce->sha1, &type, size); @@ -93,36 +93,51 @@ static void *read_blob_entry(struct cache_entry *ce, const char *path, unsigned static int write_entry(struct cache_entry *ce, char *path, const struct checkout *state, int to_tempfile) { - int fd; - long wrote; - - switch (ce->ce_mode & S_IFMT) { - char *new; - struct strbuf buf; - unsigned long size; + unsigned int ce_mode_s_ifmt = ce->ce_mode & S_IFMT; + int fd, ret; + char *new; + struct strbuf buf = STRBUF_INIT; + unsigned long size; + size_t wrote, newsize = 0; + switch (ce_mode_s_ifmt) { case S_IFREG: - new = read_blob_entry(ce, path, &size); + case S_IFLNK: + new = read_blob_entry(ce, &size); if (!new) return error("git checkout-index: unable to read sha1 file of %s (%s)", path, sha1_to_hex(ce->sha1)); + if (ce_mode_s_ifmt == S_IFLNK && has_symlinks && !to_tempfile) { + ret = symlink(new, path); + free(new); + if (ret) + return error("git checkout-index: unable to create symlink %s (%s)", + path, strerror(errno)); + break; + } + /* * Convert from git internal format to working tree format */ - strbuf_init(&buf, 0); - if (convert_to_working_tree(ce->name, new, size, &buf)) { - size_t newsize = 0; + if (ce_mode_s_ifmt == S_IFREG && + convert_to_working_tree(ce->name, new, size, &buf)) { free(new); new = strbuf_detach(&buf, &newsize); size = newsize; } if (to_tempfile) { - strcpy(path, ".merge_file_XXXXXX"); + if (ce_mode_s_ifmt == S_IFREG) + strcpy(path, ".merge_file_XXXXXX"); + else + strcpy(path, ".merge_link_XXXXXX"); fd = mkstemp(path); - } else + } else if (ce_mode_s_ifmt == S_IFREG) { fd = create_file(path, ce->ce_mode); + } else { + fd = create_file(path, 0666); + } if (fd < 0) { free(new); return error("git checkout-index: unable to create file %s (%s)", @@ -135,36 +150,6 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout if (wrote != size) return error("git checkout-index: unable to write file %s", path); break; - case S_IFLNK: - new = read_blob_entry(ce, path, &size); - if (!new) - return error("git checkout-index: unable to read sha1 file of %s (%s)", - path, sha1_to_hex(ce->sha1)); - if (to_tempfile || !has_symlinks) { - if (to_tempfile) { - strcpy(path, ".merge_link_XXXXXX"); - fd = mkstemp(path); - } else - fd = create_file(path, 0666); - if (fd < 0) { - free(new); - return error("git checkout-index: unable to create " - "file %s (%s)", path, strerror(errno)); - } - wrote = write_in_full(fd, new, size); - close(fd); - free(new); - if (wrote != size) - return error("git checkout-index: unable to write file %s", - path); - } else { - wrote = symlink(new, path); - free(new); - if (wrote) - return error("git checkout-index: unable to create " - "symlink %s (%s)", path, strerror(errno)); - } - break; case S_IFGITLINK: if (to_tempfile) return error("git checkout-index: cannot create temporary subproject %s", path); From e4c7292353dbef39feac1c6a60c5cde9140520a6 Mon Sep 17 00:00:00 2001 From: Kjetil Barvik Date: Mon, 9 Feb 2009 21:54:51 +0100 Subject: [PATCH 007/289] write_entry(): use fstat() instead of lstat() when file is open Currently inside write_entry() we do an lstat(path, &st) call on a file which have just been opened inside the exact same function. It should be better to call fstat(fd, &st) on the file while it is open, and it should be at least as fast as the lstat() method. Signed-off-by: Kjetil Barvik Signed-off-by: Junio C Hamano --- entry.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/entry.c b/entry.c index 1f53588a11..5daacc2fe5 100644 --- a/entry.c +++ b/entry.c @@ -94,11 +94,12 @@ static void *read_blob_entry(struct cache_entry *ce, unsigned long *size) static int write_entry(struct cache_entry *ce, char *path, const struct checkout *state, int to_tempfile) { unsigned int ce_mode_s_ifmt = ce->ce_mode & S_IFMT; - int fd, ret; + int fd, ret, fstat_done = 0; char *new; struct strbuf buf = STRBUF_INIT; unsigned long size; size_t wrote, newsize = 0; + struct stat st; switch (ce_mode_s_ifmt) { case S_IFREG: @@ -145,6 +146,11 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout } wrote = write_in_full(fd, new, size); + /* use fstat() only when path == ce->name */ + if (state->refresh_cache && !to_tempfile && !state->base_dir_len) { + fstat(fd, &st); + fstat_done = 1; + } close(fd); free(new); if (wrote != size) @@ -161,8 +167,8 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout } if (state->refresh_cache) { - struct stat st; - lstat(ce->name, &st); + if (!fstat_done) + lstat(ce->name, &st); fill_stat_cache_info(ce, &st); } return 0; From 91fcbcbdcdd845bd43104c5ac0af4c40da15223b Mon Sep 17 00:00:00 2001 From: Kjetil Barvik Date: Mon, 9 Feb 2009 21:54:52 +0100 Subject: [PATCH 008/289] show_patch_diff(): remove a call to fstat() Currently inside show_patch_diff() we have an fstat() call after an ok lstat() call. Since before the call to fstat() we have already tested for the link case with S_ISLNK(), the fstat() can be removed. Signed-off-by: Kjetil Barvik Signed-off-by: Junio C Hamano --- combine-diff.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/combine-diff.c b/combine-diff.c index bccc018ab2..4300319cc0 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -713,9 +713,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent, result_size = buf.len; result = strbuf_detach(&buf, NULL); elem->mode = canon_mode(st.st_mode); - } - else if (0 <= (fd = open(elem->path, O_RDONLY)) && - !fstat(fd, &st)) { + } else if (0 <= (fd = open(elem->path, O_RDONLY))) { size_t len = xsize_t(st.st_size); ssize_t done; int is_file, i; From 7734f04873cfaddd0b148074a633f1f824fd961f Mon Sep 17 00:00:00 2001 From: Kjetil Barvik Date: Mon, 9 Feb 2009 21:54:53 +0100 Subject: [PATCH 009/289] lstat_cache(): print a warning if doing ping-pong between cache types This is a debug patch which is only to be used while the lstat_cache() is in the test stage, and should be removed/reverted before the final relase. I think it should be useful to catch these warnings, as I it could be an indication of that the cache would not be very effective if it is doing ping-pong by switching between different cache types too many times. Also, if someone is experimenting with the lstat_cache(), this patch will maybe be useful while debugging. If someone is able to trigger the warning, then send a mail to the GIT mailing list, containing the first 15 lines of the warning, and a short description of the GIT commands to trigger the warnings. I hope someone is willing to use this patch for a while, to be able to catch possible ping-pong's. Signed-off-by: Kjetil Barvik Signed-off-by: Junio C Hamano --- symlinks.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/symlinks.c b/symlinks.c index 1d6b35b858..cb255a3187 100644 --- a/symlinks.c +++ b/symlinks.c @@ -51,6 +51,11 @@ static inline void reset_lstat_cache(void) */ } +#define SWITCHES_BEFORE_WARNING 10 +static unsigned int cache_switches, number_of_warnings; +static unsigned int current_cache_func, last_cache_func; +static unsigned int total_calls; + #define FL_DIR (1 << 0) #define FL_NOENT (1 << 1) #define FL_SYMLINK (1 << 2) @@ -77,6 +82,7 @@ static int lstat_cache(const char *name, int len, int match_flags, ret_flags, save_flags, max_len, ret; struct stat st; + total_calls++; if (cache.track_flags != track_flags || cache.prefix_len_stat_func != prefix_len_stat_func) { /* @@ -88,6 +94,17 @@ static int lstat_cache(const char *name, int len, cache.track_flags = track_flags; cache.prefix_len_stat_func = prefix_len_stat_func; match_len = last_slash = 0; + cache_switches++; + if (cache_switches > SWITCHES_BEFORE_WARNING) { + if (number_of_warnings < 10 || number_of_warnings % 1000 == 0) + printf("warning from %s:%d cache_switches:%u > %u "\ + "(current:%u last:%u total:%u)\n", + __FILE__, __LINE__, + cache_switches, SWITCHES_BEFORE_WARNING, + current_cache_func, last_cache_func, + total_calls); + number_of_warnings++; + } } else { /* * Check to see if we have a match from the cache for @@ -216,6 +233,8 @@ void clear_lstat_cache(void) */ int has_symlink_leading_path(const char *name, int len) { + last_cache_func = current_cache_func; + current_cache_func = 1; return lstat_cache(name, len, FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) & FL_SYMLINK; @@ -227,6 +246,8 @@ int has_symlink_leading_path(const char *name, int len) */ int has_symlink_or_noent_leading_path(const char *name, int len) { + last_cache_func = current_cache_func; + current_cache_func = 2; return lstat_cache(name, len, FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT) & (FL_SYMLINK|FL_NOENT); @@ -241,6 +262,8 @@ int has_symlink_or_noent_leading_path(const char *name, int len) */ int has_dirs_only_path(const char *name, int len, int prefix_len) { + last_cache_func = current_cache_func; + current_cache_func = 3; return lstat_cache(name, len, FL_DIR|FL_FULLPATH, prefix_len) & FL_DIR; From 7c4c97c0ac0cd66861d0c2b8bd7a47ed3c523ea7 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 16 Feb 2009 13:20:25 +0100 Subject: [PATCH 010/289] Turn the flags in struct dir_struct into a single variable By having flags represented as bits in the new member variable 'flags', it will be easier to use parse_options when dir_struct is involved. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-add.c | 2 +- builtin-checkout.c | 2 +- builtin-clean.c | 4 ++-- builtin-ls-files.c | 14 ++++++++------ builtin-merge.c | 2 +- builtin-read-tree.c | 2 +- dir.c | 17 +++++++++-------- dir.h | 12 +++++++----- wt-status.c | 7 +++---- 9 files changed, 33 insertions(+), 29 deletions(-) diff --git a/builtin-add.c b/builtin-add.c index ac98c8354d..c986a3ada1 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -104,7 +104,7 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec, /* Set up the default git porcelain excludes */ memset(dir, 0, sizeof(*dir)); if (!ignored_too) { - dir->collect_ignored = 1; + dir->flags |= DIR_COLLECT_IGNORED; setup_standard_excludes(dir); } diff --git a/builtin-checkout.c b/builtin-checkout.c index 20b34ce6e1..5b4921d48c 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -405,7 +405,7 @@ static int merge_working_tree(struct checkout_opts *opts, topts.verbose_update = !opts->quiet; topts.fn = twoway_merge; topts.dir = xcalloc(1, sizeof(*topts.dir)); - topts.dir->show_ignored = 1; + topts.dir->flags |= DIR_SHOW_IGNORED; topts.dir->exclude_per_dir = ".gitignore"; tree = parse_tree_indirect(old->commit->object.sha1); init_tree_desc(&trees[0], tree->buffer, tree->size); diff --git a/builtin-clean.c b/builtin-clean.c index f78c2fb108..c5ad33d3e6 100644 --- a/builtin-clean.c +++ b/builtin-clean.c @@ -60,7 +60,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) memset(&dir, 0, sizeof(dir)); if (ignored_only) - dir.show_ignored = 1; + dir.flags |= DIR_SHOW_IGNORED; if (ignored && ignored_only) die("-x and -X cannot be used together"); @@ -69,7 +69,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) die("clean.requireForce%s set and -n or -f not given; " "refusing to clean", config_set ? "" : " not"); - dir.show_other_directories = 1; + dir.flags |= DIR_SHOW_OTHER_DIRECTORIES; if (!ignored) setup_standard_excludes(&dir); diff --git a/builtin-ls-files.c b/builtin-ls-files.c index 9dec282fba..d36f80b3de 100644 --- a/builtin-ls-files.c +++ b/builtin-ls-files.c @@ -174,7 +174,8 @@ static void show_files(struct dir_struct *dir, const char *prefix) for (i = 0; i < active_nr; i++) { struct cache_entry *ce = active_cache[i]; int dtype = ce_to_dtype(ce); - if (excluded(dir, ce->name, &dtype) != dir->show_ignored) + if (excluded(dir, ce->name, &dtype) != + !!(dir->flags & DIR_SHOW_IGNORED)) continue; if (show_unmerged && !ce_stage(ce)) continue; @@ -189,7 +190,8 @@ static void show_files(struct dir_struct *dir, const char *prefix) struct stat st; int err; int dtype = ce_to_dtype(ce); - if (excluded(dir, ce->name, &dtype) != dir->show_ignored) + if (excluded(dir, ce->name, &dtype) != + !!(dir->flags & DIR_SHOW_IGNORED)) continue; if (ce->ce_flags & CE_UPDATE) continue; @@ -432,7 +434,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix) continue; } if (!strcmp(arg, "-i") || !strcmp(arg, "--ignored")) { - dir.show_ignored = 1; + dir.flags |= DIR_SHOW_IGNORED; require_work_tree = 1; continue; } @@ -446,11 +448,11 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix) continue; } if (!strcmp(arg, "--directory")) { - dir.show_other_directories = 1; + dir.flags |= DIR_SHOW_OTHER_DIRECTORIES; continue; } if (!strcmp(arg, "--no-empty-directory")) { - dir.hide_empty_directories = 1; + dir.flags |= DIR_HIDE_EMPTY_DIRECTORIES; continue; } if (!strcmp(arg, "-u") || !strcmp(arg, "--unmerged")) { @@ -542,7 +544,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix) ps_matched = xcalloc(1, num); } - if (dir.show_ignored && !exc_given) { + if ((dir.flags & DIR_SHOW_IGNORED) && !exc_given) { fprintf(stderr, "%s: --ignored needs some exclude pattern\n", argv[0]); exit(1); diff --git a/builtin-merge.c b/builtin-merge.c index 6d2160d0a3..4c119359e7 100644 --- a/builtin-merge.c +++ b/builtin-merge.c @@ -636,7 +636,7 @@ static int checkout_fast_forward(unsigned char *head, unsigned char *remote) memset(&opts, 0, sizeof(opts)); memset(&t, 0, sizeof(t)); memset(&dir, 0, sizeof(dir)); - dir.show_ignored = 1; + dir.flags |= DIR_SHOW_IGNORED; dir.exclude_per_dir = ".gitignore"; opts.dir = &dir; diff --git a/builtin-read-tree.c b/builtin-read-tree.c index 38fef34d3f..8e0273864d 100644 --- a/builtin-read-tree.c +++ b/builtin-read-tree.c @@ -170,7 +170,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) die("more than one --exclude-per-directory are given."); dir = xcalloc(1, sizeof(*opts.dir)); - dir->show_ignored = 1; + dir->flags |= DIR_SHOW_IGNORED; dir->exclude_per_dir = arg + 24; opts.dir = dir; /* We do not need to nor want to do read-directory diff --git a/dir.c b/dir.c index cfd1ea587d..5011cb4dd4 100644 --- a/dir.c +++ b/dir.c @@ -487,14 +487,14 @@ static enum directory_treatment treat_directory(struct dir_struct *dir, return recurse_into_directory; case index_gitdir: - if (dir->show_other_directories) + if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES) return ignore_directory; return show_directory; case index_nonexistent: - if (dir->show_other_directories) + if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES) break; - if (!dir->no_gitlinks) { + if (!(dir->flags & DIR_NO_GITLINKS)) { unsigned char sha1[20]; if (resolve_gitlink_ref(dirname, "HEAD", sha1) == 0) return show_directory; @@ -503,7 +503,7 @@ static enum directory_treatment treat_directory(struct dir_struct *dir, } /* This is the "show_other_directories" case */ - if (!dir->hide_empty_directories) + if (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES)) return show_directory; if (!read_directory_recursive(dir, dirname, dirname, len, 1, simplify)) return ignore_directory; @@ -601,7 +601,7 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co dtype = DTYPE(de); exclude = excluded(dir, fullname, &dtype); - if (exclude && dir->collect_ignored + if (exclude && (dir->flags & DIR_COLLECT_IGNORED) && in_pathspec(fullname, baselen + len, simplify)) dir_add_ignored(dir, fullname, baselen + len); @@ -609,7 +609,7 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co * Excluded? If we don't explicitly want to show * ignored files, ignore it */ - if (exclude && !dir->show_ignored) + if (exclude && !(dir->flags & DIR_SHOW_IGNORED)) continue; if (dtype == DT_UNKNOWN) @@ -621,7 +621,7 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co * even if we don't ignore them, since the * directory may contain files that we do.. */ - if (!exclude && dir->show_ignored) { + if (!exclude && (dir->flags & DIR_SHOW_IGNORED)) { if (dtype != DT_DIR) continue; } @@ -634,7 +634,8 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co len++; switch (treat_directory(dir, fullname, baselen + len, simplify)) { case show_directory: - if (exclude != dir->show_ignored) + if (exclude != !!(dir->flags + & DIR_SHOW_IGNORED)) continue; break; case recurse_into_directory: diff --git a/dir.h b/dir.h index bdc2d47447..541286ad1d 100644 --- a/dir.h +++ b/dir.h @@ -34,11 +34,13 @@ struct exclude_stack { struct dir_struct { int nr, alloc; int ignored_nr, ignored_alloc; - unsigned int show_ignored:1, - show_other_directories:1, - hide_empty_directories:1, - no_gitlinks:1, - collect_ignored:1; + enum { + DIR_SHOW_IGNORED = 1<<0, + DIR_SHOW_OTHER_DIRECTORIES = 1<<1, + DIR_HIDE_EMPTY_DIRECTORIES = 1<<2, + DIR_NO_GITLINKS = 1<<3, + DIR_COLLECT_IGNORED = 1<<4 + } flags; struct dir_entry **entries; struct dir_entry **ignored; diff --git a/wt-status.c b/wt-status.c index 96ff2f8f56..f2eae20d89 100644 --- a/wt-status.c +++ b/wt-status.c @@ -250,10 +250,9 @@ static void wt_status_print_untracked(struct wt_status *s) memset(&dir, 0, sizeof(dir)); - if (!s->untracked) { - dir.show_other_directories = 1; - dir.hide_empty_directories = 1; - } + if (!s->untracked) + dir.flags |= + DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES; setup_standard_excludes(&dir); read_directory(&dir, ".", "", 0, NULL); From ce8e8804068772a24354ee24641ca65ffa417837 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Tue, 17 Feb 2009 15:27:11 +0100 Subject: [PATCH 011/289] parse-opt: migrate builtin-ls-files. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- builtin-ls-files.c | 255 +++++++++++++++++++++------------------------ 1 file changed, 120 insertions(+), 135 deletions(-) diff --git a/builtin-ls-files.c b/builtin-ls-files.c index d36f80b3de..1742c0f80d 100644 --- a/builtin-ls-files.c +++ b/builtin-ls-files.c @@ -10,6 +10,7 @@ #include "dir.h" #include "builtin.h" #include "tree.h" +#include "parse-options.h" static int abbrev; static int show_deleted; @@ -28,6 +29,7 @@ static const char **pathspec; static int error_unmatch; static char *ps_matched; static const char *with_tree; +static int exc_given; static const char *tag_cached = ""; static const char *tag_unmerged = ""; @@ -376,156 +378,139 @@ int report_path_error(const char *ps_matched, const char **pathspec, int prefix_ return errors; } -static const char ls_files_usage[] = - "git ls-files [-z] [-t] [-v] (--[cached|deleted|others|stage|unmerged|killed|modified])* " - "[ --ignored ] [--exclude=] [--exclude-from=] " - "[ --exclude-per-directory= ] [--exclude-standard] " - "[--full-name] [--abbrev] [--] []*"; +static const char * const ls_files_usage[] = { + "git ls-files [options] []*", + NULL +}; + +static int option_parse_z(const struct option *opt, + const char *arg, int unset) +{ + line_terminator = unset ? '\n' : '\0'; + + return 0; +} + +static int option_parse_exclude(const struct option *opt, + const char *arg, int unset) +{ + struct exclude_list *list = opt->value; + + exc_given = 1; + add_exclude(arg, "", 0, list); + + return 0; +} + +static int option_parse_exclude_from(const struct option *opt, + const char *arg, int unset) +{ + struct dir_struct *dir = opt->value; + + exc_given = 1; + add_excludes_from_file(dir, arg); + + return 0; +} + +static int option_parse_exclude_standard(const struct option *opt, + const char *arg, int unset) +{ + struct dir_struct *dir = opt->value; + + exc_given = 1; + setup_standard_excludes(dir); + + return 0; +} int cmd_ls_files(int argc, const char **argv, const char *prefix) { - int i; - int exc_given = 0, require_work_tree = 0; + int require_work_tree = 0, show_tag = 0; struct dir_struct dir; + struct option builtin_ls_files_options[] = { + { OPTION_CALLBACK, 'z', NULL, NULL, NULL, + "paths are separated with NUL character", + PARSE_OPT_NOARG, option_parse_z }, + OPT_BOOLEAN('t', NULL, &show_tag, + "identify the file status with tags"), + OPT_BOOLEAN('v', NULL, &show_valid_bit, + "use lowercase letters for 'assume unchanged' files"), + OPT_BOOLEAN('c', "cached", &show_cached, + "show cached files in the output (default)"), + OPT_BOOLEAN('d', "deleted", &show_deleted, + "show deleted files in the output"), + OPT_BOOLEAN('m', "modified", &show_modified, + "show modified files in the output"), + OPT_BOOLEAN('o', "others", &show_others, + "show other files in the output"), + OPT_BIT('i', "ignored", &dir.flags, + "show ignored files in the output", + DIR_SHOW_IGNORED), + OPT_BOOLEAN('s', "stage", &show_stage, + "show staged contents' object name in the output"), + OPT_BOOLEAN('k', "killed", &show_killed, + "show files on the filesystem that need to be removed"), + OPT_BIT(0, "directory", &dir.flags, + "show 'other' directories' name only", + DIR_SHOW_OTHER_DIRECTORIES), + OPT_BIT(0, "empty-directory", &dir.flags, + "list empty directories", + DIR_HIDE_EMPTY_DIRECTORIES), + OPT_BOOLEAN('u', "unmerged", &show_unmerged, + "show unmerged files in the output"), + { OPTION_CALLBACK, 'x', "exclude", &dir.exclude_list[EXC_CMDL], "pattern", + "skip files matching pattern", + 0, option_parse_exclude }, + { OPTION_CALLBACK, 'X', "exclude-from", &dir, "file", + "exclude patterns are read from ", + 0, option_parse_exclude_from }, + OPT_STRING(0, "exclude-per-directory", &dir.exclude_per_dir, "file", + "read additional per-directory exclude patterns in "), + { OPTION_CALLBACK, 0, "exclude-standard", &dir, NULL, + "add the standard git exclusions", + PARSE_OPT_NOARG, option_parse_exclude_standard }, + { OPTION_SET_INT, 0, "full-name", &prefix_offset, NULL, + "make the output relative to the project top directory", + PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL }, + OPT_BOOLEAN(0, "error-unmatch", &error_unmatch, + "if any is not in the index, treat this as an error"), + OPT_STRING(0, "with-tree", &with_tree, "tree-ish", + "pretend that paths removed since are still present"), + OPT__ABBREV(&abbrev), + OPT_END() + }; memset(&dir, 0, sizeof(dir)); if (prefix) prefix_offset = strlen(prefix); git_config(git_default_config, NULL); - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - - if (!strcmp(arg, "--")) { - i++; - break; - } - if (!strcmp(arg, "-z")) { - line_terminator = 0; - continue; - } - if (!strcmp(arg, "-t") || !strcmp(arg, "-v")) { - tag_cached = "H "; - tag_unmerged = "M "; - tag_removed = "R "; - tag_modified = "C "; - tag_other = "? "; - tag_killed = "K "; - if (arg[1] == 'v') - show_valid_bit = 1; - continue; - } - if (!strcmp(arg, "-c") || !strcmp(arg, "--cached")) { - show_cached = 1; - continue; - } - if (!strcmp(arg, "-d") || !strcmp(arg, "--deleted")) { - show_deleted = 1; - continue; - } - if (!strcmp(arg, "-m") || !strcmp(arg, "--modified")) { - show_modified = 1; - require_work_tree = 1; - continue; - } - if (!strcmp(arg, "-o") || !strcmp(arg, "--others")) { - show_others = 1; - require_work_tree = 1; - continue; - } - if (!strcmp(arg, "-i") || !strcmp(arg, "--ignored")) { - dir.flags |= DIR_SHOW_IGNORED; - require_work_tree = 1; - continue; - } - if (!strcmp(arg, "-s") || !strcmp(arg, "--stage")) { - show_stage = 1; - continue; - } - if (!strcmp(arg, "-k") || !strcmp(arg, "--killed")) { - show_killed = 1; - require_work_tree = 1; - continue; - } - if (!strcmp(arg, "--directory")) { - dir.flags |= DIR_SHOW_OTHER_DIRECTORIES; - continue; - } - if (!strcmp(arg, "--no-empty-directory")) { - dir.flags |= DIR_HIDE_EMPTY_DIRECTORIES; - continue; - } - if (!strcmp(arg, "-u") || !strcmp(arg, "--unmerged")) { - /* There's no point in showing unmerged unless - * you also show the stage information. - */ - show_stage = 1; - show_unmerged = 1; - continue; - } - if (!strcmp(arg, "-x") && i+1 < argc) { - exc_given = 1; - add_exclude(argv[++i], "", 0, &dir.exclude_list[EXC_CMDL]); - continue; - } - if (!prefixcmp(arg, "--exclude=")) { - exc_given = 1; - add_exclude(arg+10, "", 0, &dir.exclude_list[EXC_CMDL]); - continue; - } - if (!strcmp(arg, "-X") && i+1 < argc) { - exc_given = 1; - add_excludes_from_file(&dir, argv[++i]); - continue; - } - if (!prefixcmp(arg, "--exclude-from=")) { - exc_given = 1; - add_excludes_from_file(&dir, arg+15); - continue; - } - if (!prefixcmp(arg, "--exclude-per-directory=")) { - exc_given = 1; - dir.exclude_per_dir = arg + 24; - continue; - } - if (!strcmp(arg, "--exclude-standard")) { - exc_given = 1; - setup_standard_excludes(&dir); - continue; - } - if (!strcmp(arg, "--full-name")) { - prefix_offset = 0; - continue; - } - if (!strcmp(arg, "--error-unmatch")) { - error_unmatch = 1; - continue; - } - if (!prefixcmp(arg, "--with-tree=")) { - with_tree = arg + 12; - continue; - } - if (!prefixcmp(arg, "--abbrev=")) { - abbrev = strtoul(arg+9, NULL, 10); - if (abbrev && abbrev < MINIMUM_ABBREV) - abbrev = MINIMUM_ABBREV; - else if (abbrev > 40) - abbrev = 40; - continue; - } - if (!strcmp(arg, "--abbrev")) { - abbrev = DEFAULT_ABBREV; - continue; - } - if (*arg == '-') - usage(ls_files_usage); - break; + argc = parse_options(argc, argv, builtin_ls_files_options, + ls_files_usage, 0); + if (show_tag || show_valid_bit) { + tag_cached = "H "; + tag_unmerged = "M "; + tag_removed = "R "; + tag_modified = "C "; + tag_other = "? "; + tag_killed = "K "; } + if (show_modified || show_others || (dir.flags & DIR_SHOW_IGNORED) || show_killed) + require_work_tree = 1; + if (show_unmerged) + /* + * There's no point in showing unmerged unless + * you also show the stage information. + */ + show_stage = 1; + if (dir.exclude_per_dir) + exc_given = 1; if (require_work_tree && !is_inside_work_tree()) setup_work_tree(); - pathspec = get_pathspec(prefix, argv + i); + pathspec = get_pathspec(prefix, argv); /* be nice with submodule patsh ending in a slash */ read_cache(); From 36419c8ee41cecadf67dfeab2808ff2e5025ca52 Mon Sep 17 00:00:00 2001 From: Kjetil Barvik Date: Wed, 18 Feb 2009 23:18:03 +0100 Subject: [PATCH 012/289] check_updates(): effective removal of cache entries marked CE_REMOVE Below is oprofile output from GIT command 'git chekcout -q my-v2.6.25' (move from tag v2.6.27 to tag v2.6.25 of the Linux kernel): CPU: Core 2, speed 1999.95 MHz (estimated) Counted CPU_CLK_UNHALTED events (Clock cycles when not halted) with a unit mask of 0x00 (Unhalted core cycles) count 20000 Counted INST_RETIRED_ANY_P events (number of instructions retired) with a unit mask of 0x00 (No unit mask) count 20000 CPU_CLK_UNHALT...|INST_RETIRED:2...| samples| %| samples| %| ------------------------------------ 409247 100.000 342878 100.000 git CPU_CLK_UNHALT...|INST_RETIRED:2...| samples| %| samples| %| ------------------------------------ 260476 63.6476 257843 75.1996 libz.so.1.2.3 100876 24.6492 64378 18.7758 kernel-2.6.28.4_2.vmlinux 30850 7.5382 7874 2.2964 libc-2.9.so 14775 3.6103 8390 2.4469 git 2020 0.4936 4325 1.2614 libcrypto.so.0.9.8 191 0.0467 32 0.0093 libpthread-2.9.so 58 0.0142 36 0.0105 ld-2.9.so 1 2.4e-04 0 0 libldap-2.3.so.0.2.31 Detail list of the top 20 function entries (libz counted in one blob): CPU_CLK_UNHALTED INST_RETIRED_ANY_P samples % samples % image name symbol name 260476 63.6862 257843 75.2725 libz.so.1.2.3 /lib/libz.so.1.2.3 16587 4.0555 3636 1.0615 libc-2.9.so memcpy 7710 1.8851 277 0.0809 libc-2.9.so memmove 3679 0.8995 1108 0.3235 kernel-2.6.28.4_2.vmlinux d_validate 3546 0.8670 2607 0.7611 kernel-2.6.28.4_2.vmlinux __getblk 3174 0.7760 1813 0.5293 libc-2.9.so _int_malloc 2396 0.5858 3681 1.0746 kernel-2.6.28.4_2.vmlinux copy_to_user 2270 0.5550 2528 0.7380 kernel-2.6.28.4_2.vmlinux __link_path_walk 2205 0.5391 1797 0.5246 kernel-2.6.28.4_2.vmlinux ext4_mark_iloc_dirty 2103 0.5142 1203 0.3512 kernel-2.6.28.4_2.vmlinux find_first_zero_bit 2077 0.5078 997 0.2911 kernel-2.6.28.4_2.vmlinux do_get_write_access 2070 0.5061 514 0.1501 git cache_name_compare 2043 0.4995 1501 0.4382 kernel-2.6.28.4_2.vmlinux rcu_irq_exit 2022 0.4944 1732 0.5056 kernel-2.6.28.4_2.vmlinux __ext4_get_inode_loc 2020 0.4939 4325 1.2626 libcrypto.so.0.9.8 /usr/lib/libcrypto.so.0.9.8 1965 0.4804 1384 0.4040 git patch_delta 1708 0.4176 984 0.2873 kernel-2.6.28.4_2.vmlinux rcu_sched_grace_period 1682 0.4112 727 0.2122 kernel-2.6.28.4_2.vmlinux sysfs_slab_alias 1659 0.4056 290 0.0847 git find_pack_entry_one 1480 0.3619 1307 0.3816 kernel-2.6.28.4_2.vmlinux ext4_writepage_trans_blocks Notice the memmove line, where the CPU did 7710 / 277 = 27.8 cycles per instruction, and compared to the total cycles spent inside the source code of GIT for this command, all the memmove() calls translates to (7710 * 100) / 14775 = 52.2% of this. Retesting with a GIT program compiled for gcov usage, I found out that the memmove() calls came from remove_index_entry_at() in read-cache.c, where we have: memmove(istate->cache + pos, istate->cache + pos + 1, (istate->cache_nr - pos) * sizeof(struct cache_entry *)); remove_index_entry_at() is called 4902 times from check_updates() in unpack-trees.c, and each time called we move each cache_entry pointers (from the removed one) one step to the left. Since we have 28828 entries in the cache this time, and if we on average move half of them each time, we in total move approximately 4902 * 0.5 * 28828 * 4 = 282 629 712 bytes, or twice this amount if each pointer is 8 bytes (64 bit). OK, is seems that the function check_updates() is called 28 times, so the estimated guess above had been more correct if check_updates() had been called only once, but the point is: we get lots of bytes moved. To fix this, and use an O(N) algorithm instead, where N is the number of cache_entries, we delete/remove all entries in one loop through all entries. From a retest, the new remove_marked_cache_entries() from the patch below, ended up with the following output line from oprofile: 46 0.0105 15 0.0041 git remove_marked_cache_entries If we can trust the numbers from oprofile in this case, we saved approximately ((7710 - 46) * 20000) / (2 * 1000 * 1000 * 1000) = 0.077 seconds CPU time with this fix for this particular test. And notice that now the CPU did only 46 / 15 = 3.1 cycles/instruction. Signed-off-by: Kjetil Barvik Acked-by: Linus Torvalds Signed-off-by: Junio C Hamano --- cache.h | 1 + read-cache.c | 20 ++++++++++++++++++++ unpack-trees.c | 4 +--- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/cache.h b/cache.h index 1bf2d4bde6..770d8bc950 100644 --- a/cache.h +++ b/cache.h @@ -445,6 +445,7 @@ extern int add_index_entry(struct index_state *, struct cache_entry *ce, int opt extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really); extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name); extern int remove_index_entry_at(struct index_state *, int pos); +extern void remove_marked_cache_entries(struct index_state *istate); extern int remove_file_from_index(struct index_state *, const char *path); #define ADD_CACHE_VERBOSE 1 #define ADD_CACHE_PRETEND 2 diff --git a/read-cache.c b/read-cache.c index 940ec76fdf..59a274b464 100644 --- a/read-cache.c +++ b/read-cache.c @@ -443,6 +443,26 @@ int remove_index_entry_at(struct index_state *istate, int pos) return 1; } +/* + * Remove all cache ententries marked for removal, that is where + * CE_REMOVE is set in ce_flags. This is much more effective than + * calling remove_index_entry_at() for each entry to be removed. + */ +void remove_marked_cache_entries(struct index_state *istate) +{ + struct cache_entry **ce_array = istate->cache; + unsigned int i, j; + + for (i = j = 0; i < istate->cache_nr; i++) { + if (ce_array[i]->ce_flags & CE_REMOVE) + remove_name_hash(ce_array[i]); + else + ce_array[j++] = ce_array[i]; + } + istate->cache_changed = 1; + istate->cache_nr = j; +} + int remove_file_from_index(struct index_state *istate, const char *path) { int pos = index_name_pos(istate, path, strlen(path)); diff --git a/unpack-trees.c b/unpack-trees.c index e3c3fa12aa..273b5da0a2 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -93,11 +93,9 @@ static int check_updates(struct unpack_trees_options *o) display_progress(progress, ++cnt); if (o->update) unlink_entry(ce); - remove_index_entry_at(&o->result, i); - i--; - continue; } } + remove_marked_cache_entries(&o->result); remove_scheduled_dirs(); for (i = 0; i < index->cache_nr; i++) { From 8cd6192e16d8bf590afa1105c840b72106d72941 Mon Sep 17 00:00:00 2001 From: Kjetil Barvik Date: Thu, 19 Feb 2009 21:08:28 +0100 Subject: [PATCH 013/289] fix compile error when USE_NSEC is defined 'struct cache' does not have a 'usec' member, but a 'unsigned int nsec' member. Simmilar 'struct stat' does not have a 'st_mtim.usec' member, and we should instead use 'st_mtim.tv_nsec'. Signed-off-by: Kjetil Barvik Signed-off-by: Junio C Hamano --- builtin-fetch-pack.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c index 67fb80ec48..3b210c7fdf 100644 --- a/builtin-fetch-pack.c +++ b/builtin-fetch-pack.c @@ -802,14 +802,14 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args, mtime.sec = st.st_mtime; #ifdef USE_NSEC - mtime.usec = st.st_mtim.usec; + mtime.nsec = st.st_mtim.tv_nsec; #endif if (stat(shallow, &st)) { if (mtime.sec) die("shallow file was removed during fetch"); } else if (st.st_mtime != mtime.sec #ifdef USE_NSEC - || st.st_mtim.usec != mtime.usec + || st.st_mtim.tv_nsec != mtime.nsec #endif ) die("shallow file was changed during fetch"); From fba2f38a2c2cda458e490c18e0afbb12cbd37969 Mon Sep 17 00:00:00 2001 From: Kjetil Barvik Date: Thu, 19 Feb 2009 21:08:29 +0100 Subject: [PATCH 014/289] make USE_NSEC work as expected Since the filesystem ext4 is now defined as stable in Linux v2.6.28, and ext4 supports nanonsecond resolution timestamps natively, it is time to make USE_NSEC work as expected. This will make racy git situations less likely to happen. For 'git checkout' this means it will be less likely that we have to open, read the contents of the file into RAM, and check if file is really modified or not. The result sould be a litle less used CPU time, less pagefaults and a litle faster program, at least for 'git checkout'. Since the number of possible racy git situations would increase when disks gets faster, this patch would be more and more helpfull as times go by. For a fast Solid State Disk, this patch should be helpfull. Note that, when file operations starts to take less than 1 nanosecond, one would again start to get more racy git situations. For more info on racy git, see Documentation/technical/racy-git.txt For more info on ext4, see http://kernelnewbies.org/Ext4 Signed-off-by: Kjetil Barvik Signed-off-by: Junio C Hamano --- cache.h | 6 ++--- read-cache.c | 70 ++++++++++++++++++++++++++++++++++++++++---------- unpack-trees.c | 8 ++++-- 3 files changed, 65 insertions(+), 19 deletions(-) diff --git a/cache.h b/cache.h index 770d8bc950..2badbfedc4 100644 --- a/cache.h +++ b/cache.h @@ -140,8 +140,8 @@ struct ondisk_cache_entry_extended { }; struct cache_entry { - unsigned int ce_ctime; - unsigned int ce_mtime; + struct cache_time ce_ctime; + struct cache_time ce_mtime; unsigned int ce_dev; unsigned int ce_ino; unsigned int ce_mode; @@ -282,7 +282,7 @@ struct index_state { struct cache_entry **cache; unsigned int cache_nr, cache_alloc, cache_changed; struct cache_tree *cache_tree; - time_t timestamp; + struct cache_time timestamp; void *alloc; unsigned name_hash_initialized : 1, initialized : 1; diff --git a/read-cache.c b/read-cache.c index 59a274b464..bb07371597 100644 --- a/read-cache.c +++ b/read-cache.c @@ -67,8 +67,15 @@ void rename_index_entry_at(struct index_state *istate, int nr, const char *new_n */ void fill_stat_cache_info(struct cache_entry *ce, struct stat *st) { - ce->ce_ctime = st->st_ctime; - ce->ce_mtime = st->st_mtime; + ce->ce_ctime.sec = (unsigned int)st->st_ctime; + ce->ce_mtime.sec = (unsigned int)st->st_mtime; +#ifdef USE_NSEC + ce->ce_ctime.nsec = (unsigned int)st->st_ctim.tv_nsec; + ce->ce_mtime.nsec = (unsigned int)st->st_mtim.tv_nsec; +#else + ce->ce_ctime.nsec = 0; + ce->ce_mtime.nsec = 0; +#endif ce->ce_dev = st->st_dev; ce->ce_ino = st->st_ino; ce->ce_uid = st->st_uid; @@ -196,11 +203,18 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st) default: die("internal error: ce_mode is %o", ce->ce_mode); } - if (ce->ce_mtime != (unsigned int) st->st_mtime) + if (ce->ce_mtime.sec != (unsigned int)st->st_mtime) changed |= MTIME_CHANGED; - if (trust_ctime && ce->ce_ctime != (unsigned int) st->st_ctime) + if (trust_ctime && ce->ce_ctime.sec != (unsigned int)st->st_ctime) changed |= CTIME_CHANGED; +#ifdef USE_NSEC + if (ce->ce_mtime.nsec != (unsigned int)st->st_mtim.tv_nsec) + changed |= MTIME_CHANGED; + if (trust_ctime && ce->ce_ctime.nsec != (unsigned int)st->st_ctim.tv_nsec) + changed |= CTIME_CHANGED; +#endif + if (ce->ce_uid != (unsigned int) st->st_uid || ce->ce_gid != (unsigned int) st->st_gid) changed |= OWNER_CHANGED; @@ -232,8 +246,16 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st) static int is_racy_timestamp(const struct index_state *istate, struct cache_entry *ce) { return (!S_ISGITLINK(ce->ce_mode) && - istate->timestamp && - ((unsigned int)istate->timestamp) <= ce->ce_mtime); + istate->timestamp.sec && +#ifdef USE_NSEC + /* nanosecond timestamped files can also be racy! */ + (istate->timestamp.sec < ce->ce_mtime.sec || + (istate->timestamp.sec == ce->ce_mtime.sec && + istate->timestamp.nsec <= ce->ce_mtime.nsec)) +#else + istate->timestamp.sec <= ce->ce_mtime.sec +#endif + ); } int ie_match_stat(const struct index_state *istate, @@ -1159,8 +1181,15 @@ static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_en size_t len; const char *name; - ce->ce_ctime = ntohl(ondisk->ctime.sec); - ce->ce_mtime = ntohl(ondisk->mtime.sec); + ce->ce_ctime.sec = ntohl(ondisk->ctime.sec); + ce->ce_mtime.sec = ntohl(ondisk->mtime.sec); +#ifdef USE_NSEC + ce->ce_ctime.nsec = ntohl(ondisk->ctime.nsec); + ce->ce_mtime.nsec = ntohl(ondisk->mtime.nsec); +#else + ce->ce_ctime.nsec = 0; + ce->ce_mtime.nsec = 0; +#endif ce->ce_dev = ntohl(ondisk->dev); ce->ce_ino = ntohl(ondisk->ino); ce->ce_mode = ntohl(ondisk->mode); @@ -1226,7 +1255,8 @@ int read_index_from(struct index_state *istate, const char *path) return istate->cache_nr; errno = ENOENT; - istate->timestamp = 0; + istate->timestamp.sec = 0; + istate->timestamp.nsec = 0; fd = open(path, O_RDONLY); if (fd < 0) { if (errno == ENOENT) @@ -1278,7 +1308,13 @@ int read_index_from(struct index_state *istate, const char *path) src_offset += ondisk_ce_size(ce); dst_offset += ce_size(ce); } - istate->timestamp = st.st_mtime; + istate->timestamp.sec = st.st_mtime; +#ifdef USE_NSEC + istate->timestamp.nsec = (unsigned int)st.st_mtim.tv_nsec; +#else + istate->timestamp.nsec = 0; +#endif + while (src_offset <= mmap_size - 20 - 8) { /* After an array of active_nr index entries, * there can be arbitrary number of extended @@ -1308,14 +1344,15 @@ unmap: int is_index_unborn(struct index_state *istate) { - return (!istate->cache_nr && !istate->alloc && !istate->timestamp); + return (!istate->cache_nr && !istate->alloc && !istate->timestamp.sec); } int discard_index(struct index_state *istate) { istate->cache_nr = 0; istate->cache_changed = 0; - istate->timestamp = 0; + istate->timestamp.sec = 0; + istate->timestamp.nsec = 0; istate->name_hash_initialized = 0; free_hash(&istate->name_hash); cache_tree_free(&(istate->cache_tree)); @@ -1461,10 +1498,15 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce) struct ondisk_cache_entry *ondisk = xcalloc(1, size); char *name; - ondisk->ctime.sec = htonl(ce->ce_ctime); + ondisk->ctime.sec = htonl(ce->ce_ctime.sec); + ondisk->mtime.sec = htonl(ce->ce_mtime.sec); +#ifdef USE_NSEC + ondisk->ctime.nsec = htonl(ce->ce_ctime.nsec); + ondisk->mtime.nsec = htonl(ce->ce_mtime.nsec); +#else ondisk->ctime.nsec = 0; - ondisk->mtime.sec = htonl(ce->ce_mtime); ondisk->mtime.nsec = 0; +#endif ondisk->dev = htonl(ce->ce_dev); ondisk->ino = htonl(ce->ce_ino); ondisk->mode = htonl(ce->ce_mode); diff --git a/unpack-trees.c b/unpack-trees.c index 273b5da0a2..11902cd51d 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -360,8 +360,12 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options memset(&o->result, 0, sizeof(o->result)); o->result.initialized = 1; - if (o->src_index) - o->result.timestamp = o->src_index->timestamp; + if (o->src_index) { + o->result.timestamp.sec = o->src_index->timestamp.sec; +#ifdef USE_NSEC + o->result.timestamp.nsec = o->src_index->timestamp.nsec; +#endif + } o->merge_size = len; if (!dfc) From 1dcafcc0e639ecc69b54421bda5f2270ed2601eb Mon Sep 17 00:00:00 2001 From: Kjetil Barvik Date: Thu, 19 Feb 2009 21:08:30 +0100 Subject: [PATCH 015/289] verify_uptodate(): add ce_uptodate(ce) test If we inside verify_uptodate() can already tell from the ce entry that it is already uptodate by testing it with ce_uptodate(ce), there is no need to call lstat(2) and ie_match_stat() afterwards. And, reading from the commit log message from: commit eadb5831342bb2e756fa05c03642c4aa1929d4f5 Author: Junio C Hamano Date: Fri Jan 18 23:45:24 2008 -0800 Avoid running lstat(2) on the same cache entry. this also seems to be correct usage of the ce_uptodate() macro introduced by that patch. This will avoid lots of lstat(2) calls in some cases, for example by running the 'git checkout' command. Signed-off-by: Kjetil Barvik Signed-off-by: Junio C Hamano --- unpack-trees.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unpack-trees.c b/unpack-trees.c index 11902cd51d..9fe0cd5f9b 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -430,7 +430,7 @@ static int verify_uptodate(struct cache_entry *ce, { struct stat st; - if (o->index_only || o->reset) + if (o->index_only || o->reset || ce_uptodate(ce)) return 0; if (!lstat(ce->name, &st)) { From aa387407914a574d41df4d64328498d093337ccd Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Sat, 21 Feb 2009 02:48:55 +0200 Subject: [PATCH 016/289] git_config(): not having a per-repo config file is not an error Currently git_config() returns an error if there is no repo config file available (cwd is not a git repo); it will correctly parse the system and global config files, but still return an error. It doesn't affect anything else since almost nobody is checking for the return code (with the exception of 'git remote update'). A reorganization in 'git config' would benefit from being able to properly detect errors in git_config() without the noise generated when cwd is not a git repo. Signed-off-by: Felipe Contreras Signed-off-by: Junio C Hamano --- config.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/config.c b/config.c index e5d5b4bd06..b4aae71bd3 100644 --- a/config.c +++ b/config.c @@ -637,28 +637,37 @@ int git_config_global(void) int git_config(config_fn_t fn, void *data) { - int ret = 0; + int ret = 0, found = 0; char *repo_config = NULL; const char *home = NULL; /* Setting $GIT_CONFIG makes git read _only_ the given config file. */ if (config_exclusive_filename) return git_config_from_file(fn, config_exclusive_filename, data); - if (git_config_system() && !access(git_etc_gitconfig(), R_OK)) + if (git_config_system() && !access(git_etc_gitconfig(), R_OK)) { ret += git_config_from_file(fn, git_etc_gitconfig(), data); + found += 1; + } home = getenv("HOME"); if (git_config_global() && home) { char *user_config = xstrdup(mkpath("%s/.gitconfig", home)); - if (!access(user_config, R_OK)) + if (!access(user_config, R_OK)) { ret += git_config_from_file(fn, user_config, data); + found += 1; + } free(user_config); } repo_config = git_pathdup("config"); - ret += git_config_from_file(fn, repo_config, data); + if (!access(repo_config, R_OK)) { + ret += git_config_from_file(fn, repo_config, data); + found += 1; + } free(repo_config); + if (found == 0) + return -1; return ret; } From b408457f2e167aaf8e7a087b374357f4a74e9680 Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Sat, 21 Feb 2009 02:48:56 +0200 Subject: [PATCH 017/289] git config: trivial rename in preparation for parseopt Essentially this replaces 'file' with 'prefix' in the cases where the variable is used as a prefix, which is consistent with other git commands. When using the --list option general errors where not properly reported, only errors related with the 'file'. Now they are reported, and 'file' is irrelevant. That reduces the rest of 'file' usage to nothing, therefore now only 'prefix' remains. Suggested by Johannes Schindelin. Signed-off-by: Felipe Contreras Signed-off-by: Junio C Hamano --- builtin-config.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/builtin-config.c b/builtin-config.c index d52a057444..5074c6123e 100644 --- a/builtin-config.c +++ b/builtin-config.c @@ -178,6 +178,7 @@ static char *normalize_value(const char *key, const char *value) static int get_color_found; static const char *get_color_slot; +static const char *get_colorbool_slot; static char parsed_color[COLOR_MAXLEN]; static int git_get_color_config(const char *var, const char *value, void *cb) @@ -231,7 +232,7 @@ static int get_diff_color_found; static int git_get_colorbool_config(const char *var, const char *value, void *cb) { - if (!strcmp(var, get_color_slot)) { + if (!strcmp(var, get_colorbool_slot)) { get_colorbool_found = git_config_colorbool(var, value, stdout_is_tty); } @@ -263,11 +264,11 @@ static int get_colorbool(int argc, const char **argv) usage(git_config_set_usage); get_colorbool_found = -1; get_diff_color_found = -1; - get_color_slot = argv[0]; + get_colorbool_slot = argv[0]; git_config(git_get_colorbool_config, NULL); if (get_colorbool_found < 0) { - if (!strcmp(get_color_slot, "color.diff")) + if (!strcmp(get_colorbool_slot, "color.diff")) get_colorbool_found = get_diff_color_found; if (get_colorbool_found < 0) get_colorbool_found = git_use_color_default; @@ -281,11 +282,11 @@ static int get_colorbool(int argc, const char **argv) } } -int cmd_config(int argc, const char **argv, const char *prefix) +int cmd_config(int argc, const char **argv, const char *unused_prefix) { int nongit; char *value; - const char *file = setup_git_directory_gently(&nongit); + const char *prefix = setup_git_directory_gently(&nongit); config_exclusive_filename = getenv(CONFIG_ENVIRONMENT); @@ -299,10 +300,13 @@ int cmd_config(int argc, const char **argv, const char *prefix) else if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l")) { if (argc != 2) usage(git_config_set_usage); - if (git_config(show_all_config, NULL) < 0 && - file && errno) - die("unable to read config file %s: %s", file, - strerror(errno)); + if (git_config(show_all_config, NULL) < 0) { + if (config_exclusive_filename) + die("unable to read config file %s: %s", + config_exclusive_filename, strerror(errno)); + else + die("error processing config file(s)"); + } return 0; } else if (!strcmp(argv[1], "--global")) { @@ -319,12 +323,12 @@ int cmd_config(int argc, const char **argv, const char *prefix) else if (!strcmp(argv[1], "--file") || !strcmp(argv[1], "-f")) { if (argc < 3) usage(git_config_set_usage); - if (!is_absolute_path(argv[2]) && file) - file = prefix_filename(file, strlen(file), - argv[2]); + if (!is_absolute_path(argv[2]) && prefix) + config_exclusive_filename = prefix_filename(prefix, + strlen(prefix), + argv[2]); else - file = argv[2]; - config_exclusive_filename = file; + config_exclusive_filename = argv[2]; argc--; argv++; } From 0e854a280a0b064f48ee308ef16283e321a9aa6e Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Sat, 21 Feb 2009 02:48:57 +0200 Subject: [PATCH 018/289] git config: reorganize get_color* In preparation for parseopt. Also remove some unecessary comments since the usage is described in the documentation. Signed-off-by: Felipe Contreras Signed-off-by: Junio C Hamano --- builtin-config.c | 62 ++++++++++++++---------------------------------- 1 file changed, 18 insertions(+), 44 deletions(-) diff --git a/builtin-config.c b/builtin-config.c index 5074c6123e..a2ef5f7c08 100644 --- a/builtin-config.c +++ b/builtin-config.c @@ -192,29 +192,8 @@ static int git_get_color_config(const char *var, const char *value, void *cb) return 0; } -static int get_color(int argc, const char **argv) +static void get_color(const char *def_color) { - /* - * grab the color setting for the given slot from the configuration, - * or parse the default value if missing, and return ANSI color - * escape sequence. - * - * e.g. - * git config --get-color color.diff.whitespace "blue reverse" - */ - const char *def_color = NULL; - - switch (argc) { - default: - usage(git_config_set_usage); - case 2: - def_color = argv[1]; - /* fallthru */ - case 1: - get_color_slot = argv[0]; - break; - } - get_color_found = 0; parsed_color[0] = '\0'; git_config(git_get_color_config, NULL); @@ -223,7 +202,6 @@ static int get_color(int argc, const char **argv) color_parse(def_color, "command line", parsed_color); fputs(parsed_color, stdout); - return 0; } static int stdout_is_tty; @@ -247,24 +225,10 @@ static int git_get_colorbool_config(const char *var, const char *value, return 0; } -static int get_colorbool(int argc, const char **argv) +static int get_colorbool(int print) { - /* - * git config --get-colorbool [] - * - * returns "true" or "false" depending on how - * is configured. - */ - - if (argc == 2) - stdout_is_tty = git_config_bool("command line", argv[1]); - else if (argc == 1) - stdout_is_tty = isatty(1); - else - usage(git_config_set_usage); get_colorbool_found = -1; get_diff_color_found = -1; - get_colorbool_slot = argv[0]; git_config(git_get_colorbool_config, NULL); if (get_colorbool_found < 0) { @@ -274,12 +238,11 @@ static int get_colorbool(int argc, const char **argv) get_colorbool_found = git_use_color_default; } - if (argc == 1) { - return get_colorbool_found ? 0 : 1; - } else { + if (print) { printf("%s\n", get_colorbool_found ? "true" : "false"); return 0; - } + } else + return get_colorbool_found ? 0 : 1; } int cmd_config(int argc, const char **argv, const char *unused_prefix) @@ -363,9 +326,20 @@ int cmd_config(int argc, const char **argv, const char *unused_prefix) } return 0; } else if (!strcmp(argv[1], "--get-color")) { - return get_color(argc-2, argv+2); + if (argc > 4 || argc < 3) + usage(git_config_set_usage); + get_color_slot = argv[2]; + get_color(argv[3]); + return 0; } else if (!strcmp(argv[1], "--get-colorbool")) { - return get_colorbool(argc-2, argv+2); + if (argc == 4) + stdout_is_tty = git_config_bool("command line", argv[3]); + else if (argc == 3) + stdout_is_tty = isatty(1); + else + usage(git_config_set_usage); + get_colorbool_slot = argv[2]; + return get_colorbool(argc != 3); } else if (!strcmp(argv[1], "--edit") || !strcmp(argv[1], "-e")) { if (argc != 2) usage(git_config_set_usage); From d64ec16c2af4ddcf3985d11d5dc28a15db181de5 Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Sat, 21 Feb 2009 02:49:25 +0200 Subject: [PATCH 019/289] git config: reorganize to use parseopt This patch has benefited from comments by Johannes Schindelin and Junio C Hamano. Signed-off-by: Felipe Contreras Signed-off-by: Junio C Hamano --- builtin-config.c | 351 ++++++++++++++++++++++++++--------------------- 1 file changed, 196 insertions(+), 155 deletions(-) diff --git a/builtin-config.c b/builtin-config.c index a2ef5f7c08..08a77cd7df 100644 --- a/builtin-config.c +++ b/builtin-config.c @@ -1,9 +1,12 @@ #include "builtin.h" #include "cache.h" #include "color.h" +#include "parse-options.h" -static const char git_config_set_usage[] = -"git config [ --global | --system | [ -f | --file ] config-file ] [ --bool | --int | --bool-or-int ] [ -z | --null ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list | --get-color var [default] | --get-colorbool name [stdout-is-tty] | --edit | -e ]"; +static const char *const builtin_config_usage[] = { + "git config [options]", + NULL +}; static char *key; static regex_t *key_regexp; @@ -18,6 +21,63 @@ static char key_delim = ' '; static char term = '\n'; static enum { T_RAW, T_INT, T_BOOL, T_BOOL_OR_INT } type = T_RAW; +static int use_global_config, use_system_config; +static const char *given_config_file; +static int actions; +static const char *get_color_slot, *get_colorbool_slot; +static int end_null; + +#define ACTION_GET (1<<0) +#define ACTION_GET_ALL (1<<1) +#define ACTION_GET_REGEXP (1<<2) +#define ACTION_REPLACE_ALL (1<<3) +#define ACTION_ADD (1<<4) +#define ACTION_UNSET (1<<5) +#define ACTION_UNSET_ALL (1<<6) +#define ACTION_RENAME_SECTION (1<<7) +#define ACTION_REMOVE_SECTION (1<<8) +#define ACTION_LIST (1<<9) +#define ACTION_EDIT (1<<10) +#define ACTION_SET (1<<11) +#define ACTION_SET_ALL (1<<12) +#define ACTION_GET_COLOR (1<<13) +#define ACTION_GET_COLORBOOL (1<<14) + +static struct option builtin_config_options[] = { + OPT_GROUP("Config file location"), + OPT_BOOLEAN(0, "global", &use_global_config, "use global config file"), + OPT_BOOLEAN(0, "system", &use_system_config, "use system config file"), + OPT_STRING('f', "file", &given_config_file, "FILE", "use given config file"), + OPT_GROUP("Action"), + OPT_BIT(0, "get", &actions, "get value: name [value-regex]", ACTION_GET), + OPT_BIT(0, "get-all", &actions, "get all values: key [value-regex]", ACTION_GET_ALL), + OPT_BIT(0, "get-regexp", &actions, "get values for regexp: name-regex [value-regex]", ACTION_GET_REGEXP), + OPT_BIT(0, "replace-all", &actions, "replace all matching variables: name [value [value_regex]", ACTION_REPLACE_ALL), + OPT_BIT(0, "add", &actions, "adds a new variable: name value", ACTION_ADD), + OPT_BIT(0, "unset", &actions, "removes a variable: name [value-regex]", ACTION_UNSET), + OPT_BIT(0, "unset-all", &actions, "removes all matches: name [value-regex]", ACTION_UNSET_ALL), + OPT_BIT(0, "rename-section", &actions, "rename section: old-name new-name", ACTION_RENAME_SECTION), + OPT_BIT(0, "remove-section", &actions, "remove a section: name", ACTION_REMOVE_SECTION), + OPT_BIT('l', "list", &actions, "list all", ACTION_LIST), + OPT_BIT('e', "edit", &actions, "opens an editor", ACTION_EDIT), + OPT_STRING(0, "get-color", &get_color_slot, "slot", "find the color configured: [default]"), + OPT_STRING(0, "get-colorbool", &get_colorbool_slot, "slot", "find the color setting: [stdout-is-tty]"), + OPT_GROUP("Type"), + OPT_SET_INT(0, "bool", &type, "value is \"true\" or \"false\"", T_BOOL), + OPT_SET_INT(0, "int", &type, "value is decimal number", T_INT), + OPT_SET_INT(0, "bool-or-int", &type, NULL, T_BOOL_OR_INT), + OPT_GROUP("Other"), + OPT_BOOLEAN('z', "null", &end_null, "terminate values with NUL byte"), + OPT_END(), +}; + +static void check_argc(int argc, int min, int max) { + if (argc >= min && argc <= max) + return; + error("wrong number of arguments"); + usage_with_options(builtin_config_usage, builtin_config_options); +} + static int show_all_config(const char *key_, const char *value_, void *cb) { if (value_) @@ -253,162 +313,143 @@ int cmd_config(int argc, const char **argv, const char *unused_prefix) config_exclusive_filename = getenv(CONFIG_ENVIRONMENT); - while (1 < argc) { - if (!strcmp(argv[1], "--int")) - type = T_INT; - else if (!strcmp(argv[1], "--bool")) - type = T_BOOL; - else if (!strcmp(argv[1], "--bool-or-int")) - type = T_BOOL_OR_INT; - else if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l")) { - if (argc != 2) - usage(git_config_set_usage); - if (git_config(show_all_config, NULL) < 0) { - if (config_exclusive_filename) - die("unable to read config file %s: %s", - config_exclusive_filename, strerror(errno)); - else - die("error processing config file(s)"); - } - return 0; + argc = parse_options(argc, argv, builtin_config_options, builtin_config_usage, + PARSE_OPT_STOP_AT_NON_OPTION); + + if (use_global_config) { + char *home = getenv("HOME"); + if (home) { + char *user_config = xstrdup(mkpath("%s/.gitconfig", home)); + config_exclusive_filename = user_config; + } else { + die("$HOME not set"); } - else if (!strcmp(argv[1], "--global")) { - char *home = getenv("HOME"); - if (home) { - char *user_config = xstrdup(mkpath("%s/.gitconfig", home)); - config_exclusive_filename = user_config; - } else { - die("$HOME not set"); - } - } - else if (!strcmp(argv[1], "--system")) - config_exclusive_filename = git_etc_gitconfig(); - else if (!strcmp(argv[1], "--file") || !strcmp(argv[1], "-f")) { - if (argc < 3) - usage(git_config_set_usage); - if (!is_absolute_path(argv[2]) && prefix) - config_exclusive_filename = prefix_filename(prefix, - strlen(prefix), - argv[2]); - else - config_exclusive_filename = argv[2]; - argc--; - argv++; - } - else if (!strcmp(argv[1], "--null") || !strcmp(argv[1], "-z")) { - term = '\0'; - delim = '\n'; - key_delim = '\n'; - } - else if (!strcmp(argv[1], "--rename-section")) { - int ret; - if (argc != 4) - usage(git_config_set_usage); - ret = git_config_rename_section(argv[2], argv[3]); - if (ret < 0) - return ret; - if (ret == 0) { - fprintf(stderr, "No such section!\n"); - return 1; - } - return 0; - } - else if (!strcmp(argv[1], "--remove-section")) { - int ret; - if (argc != 3) - usage(git_config_set_usage); - ret = git_config_rename_section(argv[2], NULL); - if (ret < 0) - return ret; - if (ret == 0) { - fprintf(stderr, "No such section!\n"); - return 1; - } - return 0; - } else if (!strcmp(argv[1], "--get-color")) { - if (argc > 4 || argc < 3) - usage(git_config_set_usage); - get_color_slot = argv[2]; - get_color(argv[3]); - return 0; - } else if (!strcmp(argv[1], "--get-colorbool")) { - if (argc == 4) - stdout_is_tty = git_config_bool("command line", argv[3]); - else if (argc == 3) - stdout_is_tty = isatty(1); - else - usage(git_config_set_usage); - get_colorbool_slot = argv[2]; - return get_colorbool(argc != 3); - } else if (!strcmp(argv[1], "--edit") || !strcmp(argv[1], "-e")) { - if (argc != 2) - usage(git_config_set_usage); - git_config(git_default_config, NULL); - launch_editor(config_exclusive_filename ? - config_exclusive_filename : git_path("config"), - NULL, NULL); - return 0; - } else - break; - argc--; - argv++; + } + else if (use_system_config) + config_exclusive_filename = git_etc_gitconfig(); + else if (given_config_file) { + if (!is_absolute_path(given_config_file) && prefix) + config_exclusive_filename = prefix_filename(prefix, + strlen(prefix), + argv[2]); + else + config_exclusive_filename = given_config_file; } - switch (argc) { - case 2: - return get_value(argv[1], NULL); - case 3: - if (!strcmp(argv[1], "--unset")) - return git_config_set(argv[2], NULL); - else if (!strcmp(argv[1], "--unset-all")) - return git_config_set_multivar(argv[2], NULL, NULL, 1); - else if (!strcmp(argv[1], "--get")) - return get_value(argv[2], NULL); - else if (!strcmp(argv[1], "--get-all")) { - do_all = 1; - return get_value(argv[2], NULL); - } else if (!strcmp(argv[1], "--get-regexp")) { - show_keys = 1; - use_key_regexp = 1; - do_all = 1; - return get_value(argv[2], NULL); - } else { - value = normalize_value(argv[1], argv[2]); - return git_config_set(argv[1], value); - } - case 4: - if (!strcmp(argv[1], "--unset")) - return git_config_set_multivar(argv[2], NULL, argv[3], 0); - else if (!strcmp(argv[1], "--unset-all")) - return git_config_set_multivar(argv[2], NULL, argv[3], 1); - else if (!strcmp(argv[1], "--get")) - return get_value(argv[2], argv[3]); - else if (!strcmp(argv[1], "--get-all")) { - do_all = 1; - return get_value(argv[2], argv[3]); - } else if (!strcmp(argv[1], "--get-regexp")) { - show_keys = 1; - use_key_regexp = 1; - do_all = 1; - return get_value(argv[2], argv[3]); - } else if (!strcmp(argv[1], "--add")) { - value = normalize_value(argv[2], argv[3]); - return git_config_set_multivar(argv[2], value, "^$", 0); - } else if (!strcmp(argv[1], "--replace-all")) { - value = normalize_value(argv[2], argv[3]); - return git_config_set_multivar(argv[2], value, NULL, 1); - } else { - value = normalize_value(argv[1], argv[2]); - return git_config_set_multivar(argv[1], value, argv[3], 0); - } - case 5: - if (!strcmp(argv[1], "--replace-all")) { - value = normalize_value(argv[2], argv[3]); - return git_config_set_multivar(argv[2], value, argv[4], 1); - } - case 1: - default: - usage(git_config_set_usage); + if (end_null) { + term = '\0'; + delim = '\n'; + key_delim = '\n'; } + + if (get_color_slot) + actions |= ACTION_GET_COLOR; + if (get_colorbool_slot) + actions |= ACTION_GET_COLORBOOL; + + if (HAS_MULTI_BITS(actions)) { + error("only one action at a time."); + usage_with_options(builtin_config_usage, builtin_config_options); + } + if (actions == 0) + switch (argc) { + case 1: actions = ACTION_GET; break; + case 2: actions = ACTION_SET; break; + case 3: actions = ACTION_SET_ALL; break; + default: + usage_with_options(builtin_config_usage, builtin_config_options); + } + + if (actions == ACTION_LIST) { + if (git_config(show_all_config, NULL) < 0) { + if (config_exclusive_filename) + die("unable to read config file %s: %s", + config_exclusive_filename, strerror(errno)); + else + die("error processing config file(s)"); + } + } + else if (actions == ACTION_EDIT) { + git_config(git_default_config, NULL); + launch_editor(config_exclusive_filename ? + config_exclusive_filename : git_path("config"), + NULL, NULL); + } + else if (actions == ACTION_SET) { + check_argc(argc, 2, 2); + value = normalize_value(argv[0], argv[1]); + return git_config_set(argv[0], value); + } + else if (actions == ACTION_SET_ALL) { + check_argc(argc, 2, 3); + value = normalize_value(argv[0], argv[1]); + return git_config_set_multivar(argv[0], value, argv[2], 0); + } + else if (actions == ACTION_ADD) { + check_argc(argc, 2, 2); + value = normalize_value(argv[0], argv[1]); + return git_config_set_multivar(argv[0], value, "^$", 0); + } + else if (actions == ACTION_REPLACE_ALL) { + check_argc(argc, 2, 3); + value = normalize_value(argv[0], argv[1]); + return git_config_set_multivar(argv[0], value, argv[2], 1); + } + else if (actions == ACTION_GET) { + check_argc(argc, 1, 2); + return get_value(argv[0], argv[1]); + } + else if (actions == ACTION_GET_ALL) { + do_all = 1; + check_argc(argc, 1, 2); + return get_value(argv[0], argv[1]); + } + else if (actions == ACTION_GET_REGEXP) { + show_keys = 1; + use_key_regexp = 1; + do_all = 1; + check_argc(argc, 1, 2); + return get_value(argv[0], argv[1]); + } + else if (actions == ACTION_UNSET) { + check_argc(argc, 1, 2); + if (argc == 2) + return git_config_set_multivar(argv[0], NULL, argv[1], 0); + else + return git_config_set(argv[0], NULL); + } + else if (actions == ACTION_UNSET_ALL) { + check_argc(argc, 1, 2); + return git_config_set_multivar(argv[0], NULL, argv[1], 1); + } + else if (actions == ACTION_RENAME_SECTION) { + int ret; + check_argc(argc, 2, 2); + ret = git_config_rename_section(argv[0], argv[1]); + if (ret < 0) + return ret; + if (ret == 0) + die("No such section!"); + } + else if (actions == ACTION_REMOVE_SECTION) { + int ret; + check_argc(argc, 1, 1); + ret = git_config_rename_section(argv[0], NULL); + if (ret < 0) + return ret; + if (ret == 0) + die("No such section!"); + } + else if (actions == ACTION_GET_COLOR) { + get_color(argv[0]); + } + else if (actions == ACTION_GET_COLORBOOL) { + if (argc == 1) + stdout_is_tty = git_config_bool("command line", argv[0]); + else if (argc == 0) + stdout_is_tty = isatty(1); + return get_colorbool(argc != 0); + } + return 0; } From 67052c9dcfb3ab46b18e734ea4a9117eb61fea4e Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Sat, 21 Feb 2009 02:49:26 +0200 Subject: [PATCH 020/289] git config: don't allow multiple config file locations Either --global, --system, or --file can be used, but not any combination. Signed-off-by: Felipe Contreras Signed-off-by: Junio C Hamano --- builtin-config.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/builtin-config.c b/builtin-config.c index 08a77cd7df..d037e4745c 100644 --- a/builtin-config.c +++ b/builtin-config.c @@ -316,6 +316,11 @@ int cmd_config(int argc, const char **argv, const char *unused_prefix) argc = parse_options(argc, argv, builtin_config_options, builtin_config_usage, PARSE_OPT_STOP_AT_NON_OPTION); + if (use_global_config + use_system_config + !!given_config_file > 1) { + error("only one config file at a time."); + usage_with_options(builtin_config_usage, builtin_config_options); + } + if (use_global_config) { char *home = getenv("HOME"); if (home) { From 16c1e939856f6ced97c60beb64936d564d198be3 Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Sat, 21 Feb 2009 02:49:27 +0200 Subject: [PATCH 021/289] git config: don't allow multiple variable types Only --bool, --int, or --bool-or-int can be used, but not any combination of them. Signed-off-by: Felipe Contreras Signed-off-by: Junio C Hamano --- builtin-config.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/builtin-config.c b/builtin-config.c index d037e4745c..6dc205d1f4 100644 --- a/builtin-config.c +++ b/builtin-config.c @@ -19,11 +19,10 @@ static int seen; static char delim = '='; static char key_delim = ' '; static char term = '\n'; -static enum { T_RAW, T_INT, T_BOOL, T_BOOL_OR_INT } type = T_RAW; static int use_global_config, use_system_config; static const char *given_config_file; -static int actions; +static int actions, types; static const char *get_color_slot, *get_colorbool_slot; static int end_null; @@ -43,6 +42,10 @@ static int end_null; #define ACTION_GET_COLOR (1<<13) #define ACTION_GET_COLORBOOL (1<<14) +#define TYPE_BOOL (1<<0) +#define TYPE_INT (1<<1) +#define TYPE_BOOL_OR_INT (1<<2) + static struct option builtin_config_options[] = { OPT_GROUP("Config file location"), OPT_BOOLEAN(0, "global", &use_global_config, "use global config file"), @@ -63,9 +66,9 @@ static struct option builtin_config_options[] = { OPT_STRING(0, "get-color", &get_color_slot, "slot", "find the color configured: [default]"), OPT_STRING(0, "get-colorbool", &get_colorbool_slot, "slot", "find the color setting: [stdout-is-tty]"), OPT_GROUP("Type"), - OPT_SET_INT(0, "bool", &type, "value is \"true\" or \"false\"", T_BOOL), - OPT_SET_INT(0, "int", &type, "value is decimal number", T_INT), - OPT_SET_INT(0, "bool-or-int", &type, NULL, T_BOOL_OR_INT), + OPT_BIT(0, "bool", &types, "value is \"true\" or \"false\"", TYPE_BOOL), + OPT_BIT(0, "int", &types, "value is decimal number", TYPE_INT), + OPT_BIT(0, "bool-or-int", &types, NULL, TYPE_BOOL_OR_INT), OPT_GROUP("Other"), OPT_BOOLEAN('z', "null", &end_null, "terminate values with NUL byte"), OPT_END(), @@ -109,11 +112,11 @@ static int show_config(const char *key_, const char *value_, void *cb) } if (seen && !do_all) dup_error = 1; - if (type == T_INT) + if (types == TYPE_INT) sprintf(value, "%d", git_config_int(key_, value_?value_:"")); - else if (type == T_BOOL) + else if (types == TYPE_BOOL) vptr = git_config_bool(key_, value_) ? "true" : "false"; - else if (type == T_BOOL_OR_INT) { + else if (types == TYPE_BOOL_OR_INT) { int is_bool, v; v = git_config_bool_or_int(key_, value_, &is_bool); if (is_bool) @@ -212,18 +215,18 @@ static char *normalize_value(const char *key, const char *value) if (!value) return NULL; - if (type == T_RAW) + if (types == 0) normalized = xstrdup(value); else { normalized = xmalloc(64); - if (type == T_INT) { + if (types == TYPE_INT) { int v = git_config_int(key, value); sprintf(normalized, "%d", v); } - else if (type == T_BOOL) + else if (types == TYPE_BOOL) sprintf(normalized, "%s", git_config_bool(key, value) ? "true" : "false"); - else if (type == T_BOOL_OR_INT) { + else if (types == TYPE_BOOL_OR_INT) { int is_bool, v; v = git_config_bool_or_int(key, value, &is_bool); if (!is_bool) @@ -347,6 +350,11 @@ int cmd_config(int argc, const char **argv, const char *unused_prefix) key_delim = '\n'; } + if (HAS_MULTI_BITS(types)) { + error("only one type at a time."); + usage_with_options(builtin_config_usage, builtin_config_options); + } + if (get_color_slot) actions |= ACTION_GET_COLOR; if (get_colorbool_slot) From 225a9caf18911bffe2f32e4960c94e51f135182b Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Sat, 21 Feb 2009 02:49:28 +0200 Subject: [PATCH 022/289] git config: don't allow extra arguments for -e or -l. As suggested by Johannes Schindelin. Signed-off-by: Felipe Contreras Signed-off-by: Junio C Hamano --- builtin-config.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/builtin-config.c b/builtin-config.c index 6dc205d1f4..a3a334bc63 100644 --- a/builtin-config.c +++ b/builtin-config.c @@ -374,6 +374,7 @@ int cmd_config(int argc, const char **argv, const char *unused_prefix) } if (actions == ACTION_LIST) { + check_argc(argc, 0, 0); if (git_config(show_all_config, NULL) < 0) { if (config_exclusive_filename) die("unable to read config file %s: %s", @@ -383,6 +384,7 @@ int cmd_config(int argc, const char **argv, const char *unused_prefix) } } else if (actions == ACTION_EDIT) { + check_argc(argc, 0, 0); git_config(git_default_config, NULL); launch_editor(config_exclusive_filename ? config_exclusive_filename : git_path("config"), From c23873589483eb5dc753190309af8c5821169118 Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Sat, 21 Feb 2009 02:49:29 +0200 Subject: [PATCH 023/289] git config: don't allow --get-color* and variable type Doing so would be incoherent since --get-color would pick a color slot and ignore the variable type option (e.g. --bool), and the type would require a variable name. Suggested by Junio C Hamano. Signed-off-by: Felipe Contreras Signed-off-by: Junio C Hamano --- builtin-config.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/builtin-config.c b/builtin-config.c index a3a334bc63..b11a0961bd 100644 --- a/builtin-config.c +++ b/builtin-config.c @@ -360,6 +360,11 @@ int cmd_config(int argc, const char **argv, const char *unused_prefix) if (get_colorbool_slot) actions |= ACTION_GET_COLORBOOL; + if ((get_color_slot || get_colorbool_slot) && types) { + error("--get-color and variable type are incoherent"); + usage_with_options(builtin_config_usage, builtin_config_options); + } + if (HAS_MULTI_BITS(actions)) { error("only one action at a time."); usage_with_options(builtin_config_usage, builtin_config_options); From e1afca4fd3e7cb4000874e991277f10119de4ad2 Mon Sep 17 00:00:00 2001 From: Kjetil Barvik Date: Mon, 23 Feb 2009 19:02:57 +0100 Subject: [PATCH 024/289] write_index(): update index_state->timestamp after flushing to disk Since this timestamp is used to check for racy-clean files, it is important to keep it uptodate. For the 'git checkout' command without the '-q' option, this make a huge difference. Before, each and every file which was updated, was racy-clean after the call to unpack_trees() and write_index() but before the GIT process ended. And because of the call to show_local_changes() in builtin-checkout.c, we ended up reading those files back into memory, doing a SHA1 to check if the files was really different from the index. And, of course, no file was different. With this fix, 'git checkout' without the '-q' option should now be almost as fast as with the '-q' option, but not quite, as we still do some few lstat(2) calls more without the '-q' option. Below is some average numbers for 10 checkout's to v2.6.27 and 10 to v2.6.25 of the Linux kernel, to show the difference: before (git version 1.6.2.rc1.256.g58a87): 7.860 user 2.427 sys 19.465 real 52.8% CPU faults: 0 major 95331 minor after: 6.184 user 2.160 sys 17.619 real 47.4% CPU faults: 0 major 38994 minor Signed-off-by: Kjetil Barvik Signed-off-by: Junio C Hamano --- cache.h | 2 +- read-cache.c | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/cache.h b/cache.h index 2badbfedc4..2f4f0549f9 100644 --- a/cache.h +++ b/cache.h @@ -430,7 +430,7 @@ extern int read_index_preload(struct index_state *, const char **pathspec); 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(const struct index_state *, int newfd); +extern int write_index(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); diff --git a/read-cache.c b/read-cache.c index bb07371597..91f1d03c09 100644 --- a/read-cache.c +++ b/read-cache.c @@ -1528,13 +1528,14 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce) return ce_write(c, fd, ondisk, size); } -int write_index(const struct index_state *istate, int newfd) +int write_index(struct index_state *istate, int newfd) { git_SHA_CTX c; struct cache_header hdr; int i, err, removed, extended; struct cache_entry **cache = istate->cache; int entries = istate->cache_nr; + struct stat st; for (i = removed = extended = 0; i < entries; i++) { if (cache[i]->ce_flags & CE_REMOVE) @@ -1578,7 +1579,14 @@ int write_index(const struct index_state *istate, int newfd) if (err) return -1; } - return ce_flush(&c, newfd); + + if (ce_flush(&c, newfd) || fstat(newfd, &st)) + return -1; + istate->timestamp.sec = (unsigned int)st.st_ctime; +#ifdef USE_NSEC + istate->timestamp.nsec = (unsigned int)st.st_ctim.tv_nsec; +#endif + return 0; } /* From 75318a3bad4b7a25f617217891bfbcec26a9161a Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 25 Feb 2009 03:32:08 -0500 Subject: [PATCH 025/289] test scripts: refactor start_httpd helper There are some redirects and some error checking that need to be done by the caller; let's move both into the start_httpd function so that all callers don't have to repeat them (there is only one caller now, but another will follow in this series). This doesn't violate any assumptions that aren't already being made by lib-httpd, which is happy to say "skipping" and call test_done for a number of other cases. Signed-off-by: Jeff King Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- t/lib-httpd.sh | 9 +++++++-- t/t5540-http-push.sh | 8 +------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh index 86cdebc727..589aaf8214 100644 --- a/t/lib-httpd.sh +++ b/t/lib-httpd.sh @@ -94,13 +94,18 @@ prepare_httpd() { } start_httpd() { - prepare_httpd + prepare_httpd >&3 2>&4 trap 'stop_httpd; die' EXIT "$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \ -f "$TEST_PATH/apache.conf" $HTTPD_PARA \ - -c "Listen 127.0.0.1:$LIB_HTTPD_PORT" -k start + -c "Listen 127.0.0.1:$LIB_HTTPD_PORT" -k start \ + >&3 2>&4 + if ! test $? = 0; then + say "skipping test, web server setup failed" + test_done + fi } stop_httpd() { diff --git a/t/t5540-http-push.sh b/t/t5540-http-push.sh index 11b343274f..57a4411e98 100755 --- a/t/t5540-http-push.sh +++ b/t/t5540-http-push.sh @@ -20,13 +20,7 @@ then fi . "$TEST_DIRECTORY"/lib-httpd.sh - -if ! start_httpd >&3 2>&4 -then - say "skipping test, web server setup failed" - test_done - exit -fi +start_httpd test_expect_success 'setup remote repository' ' cd "$ROOT_PATH" && From 119c8eeede81489b2ce8b26ae7dcb47290e257eb Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 25 Feb 2009 03:32:09 -0500 Subject: [PATCH 026/289] add basic http clone/fetch tests This was mostly being tested implicitly by the "http push" tests. But making a separate test script means that: - we will run fetch tests even when http pushing support is not built - when there are failures on fetching, they are easier to see and isolate, as they are not in the middle of push tests This script defaults to running the webserver on port 5550, and puts the original t5540 on port 5540, so that the two can be run simultaneously without conflict (but both still respect an externally set LIB_HTTPD_PORT). Signed-off-by: Jeff King Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- Makefile | 1 + t/t5540-http-push.sh | 1 + t/t5550-http-fetch.sh | 46 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+) create mode 100755 t/t5550-http-fetch.sh diff --git a/Makefile b/Makefile index 0675c43e73..744ab4ff8f 100644 --- a/Makefile +++ b/Makefile @@ -1363,6 +1363,7 @@ GIT-CFLAGS: .FORCE-GIT-CFLAGS GIT-BUILD-OPTIONS: .FORCE-GIT-BUILD-OPTIONS @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@ @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@ + @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@ ### Detect Tck/Tk interpreter path changes ifndef NO_TCLTK diff --git a/t/t5540-http-push.sh b/t/t5540-http-push.sh index 57a4411e98..cefab4543a 100755 --- a/t/t5540-http-push.sh +++ b/t/t5540-http-push.sh @@ -11,6 +11,7 @@ This test runs various sanity checks on http-push.' ROOT_PATH="$PWD" LIB_HTTPD_DAV=t +LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5540'} if git http-push > /dev/null 2>&1 || [ $? -eq 128 ] then diff --git a/t/t5550-http-fetch.sh b/t/t5550-http-fetch.sh new file mode 100755 index 0000000000..b6e6ec9607 --- /dev/null +++ b/t/t5550-http-fetch.sh @@ -0,0 +1,46 @@ +#!/bin/sh + +test_description='test fetching over http' +. ./test-lib.sh + +if test -n "$NO_CURL"; then + say 'skipping test, git built without http support' + test_done +fi + +. "$TEST_DIRECTORY"/lib-httpd.sh +LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5550'} +start_httpd + +test_expect_success 'setup repository' ' + echo content >file && + git add file && + git commit -m one +' + +test_expect_success 'create http-accessible bare repository' ' + mkdir "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && + (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && + git --bare init && + echo "exec git update-server-info" >hooks/post-update && + chmod +x hooks/post-update + ) && + git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && + git push public master:master +' + +test_expect_success 'clone http repository' ' + git clone $HTTPD_URL/repo.git clone && + test_cmp file clone/file +' + +test_expect_success 'fetch changes via http' ' + echo content >>file && + git commit -a -m two && + git push public + (cd clone && git pull) && + test_cmp file clone/file +' + +stop_httpd +test_done From 5483f79998c5a9705d453a713d11fb7591329ed4 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 25 Feb 2009 03:32:10 -0500 Subject: [PATCH 027/289] refactor find_ref_by_name() to accept const list Since it doesn't actually touch its argument, this makes sense. However, we still want to return a non-const version (which requires a cast) so that this: struct ref *a, *b; a = find_ref_by_name(b); works. Unfortunately, you can also silently strip the const from a variable: struct ref *a; const struct ref *b; a = find_ref_by_name(b); This is a classic C const problem because there is no way to say "return the type with the same constness that was passed to us"; we provide the same semantics as standard library functions like strchr. Signed-off-by: Jeff King Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- cache.h | 2 +- refs.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cache.h b/cache.h index 189151de25..609380d935 100644 --- a/cache.h +++ b/cache.h @@ -801,7 +801,7 @@ struct ref { #define REF_HEADS (1u << 1) #define REF_TAGS (1u << 2) -extern struct ref *find_ref_by_name(struct ref *list, const char *name); +extern struct ref *find_ref_by_name(const struct ref *list, const char *name); #define CONNECT_VERBOSE (1u << 0) extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags); diff --git a/refs.c b/refs.c index 6eb5f53846..b2a37e1185 100644 --- a/refs.c +++ b/refs.c @@ -1628,10 +1628,10 @@ int update_ref(const char *action, const char *refname, return 0; } -struct ref *find_ref_by_name(struct ref *list, const char *name) +struct ref *find_ref_by_name(const struct ref *list, const char *name) { for ( ; list; list = list->next) if (!strcmp(list->name, name)) - return list; + return (struct ref *)list; return NULL; } From 454e2025a933593fd751475b59cc014887b4df6d Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Wed, 25 Feb 2009 03:32:11 -0500 Subject: [PATCH 028/289] move duplicated get_local_heads() to remote.c get_local_heads() appears to have been copied from builtin-send-pack.c to http-push.c via cut and paste. This patch moves the function and its helper one_local_ref() to remote.c. The two copies of one_local_ref() were not identical. I used the more recent version from builtin-send-pack.c after confirming with Jeff King that it was an oversight that commit 30affa1e did not update both copies. This is in preparation for being able to call it from builtin-remote.c Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- builtin-send-pack.c | 29 ++--------------------------- http-push.c | 23 ++--------------------- remote.c | 26 ++++++++++++++++++++++++++ remote.h | 1 + 4 files changed, 31 insertions(+), 48 deletions(-) diff --git a/builtin-send-pack.c b/builtin-send-pack.c index d65d019692..2fbfc291dc 100644 --- a/builtin-send-pack.c +++ b/builtin-send-pack.c @@ -133,33 +133,8 @@ static int ref_newer(const unsigned char *new_sha1, return found; } -static struct ref *local_refs, **local_tail; static struct ref *remote_refs, **remote_tail; -static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) -{ - struct ref *ref; - int len; - - /* we already know it starts with refs/ to get here */ - if (check_ref_format(refname + 5)) - return 0; - - len = strlen(refname) + 1; - ref = xcalloc(1, sizeof(*ref) + len); - hashcpy(ref->new_sha1, sha1); - memcpy(ref->name, refname, len); - *local_tail = ref; - local_tail = &ref->next; - return 0; -} - -static void get_local_heads(void) -{ - local_tail = &local_refs; - for_each_ref(one_local_ref, NULL); -} - static int receive_status(int in, struct ref *refs) { struct ref *hint; @@ -387,7 +362,7 @@ static int refs_pushed(struct ref *ref) static int do_send_pack(int in, int out, struct remote *remote, const char *dest, int nr_refspec, const char **refspec) { - struct ref *ref; + struct ref *ref, *local_refs; int new_refs; int ask_for_status_report = 0; int allow_deleting_refs = 0; @@ -405,7 +380,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest /* No funny business with the matcher */ remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL, &extra_have); - get_local_heads(); + local_refs = get_local_heads(); /* Does the other end support the reporting? */ if (server_supports("report-status")) diff --git a/http-push.c b/http-push.c index 30d2d34041..cfeed81d07 100644 --- a/http-push.c +++ b/http-push.c @@ -1792,21 +1792,8 @@ static int update_remote(unsigned char *sha1, struct remote_lock *lock) return 1; } -static struct ref *local_refs, **local_tail; static struct ref *remote_refs, **remote_tail; -static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) -{ - struct ref *ref; - int len = strlen(refname) + 1; - ref = xcalloc(1, sizeof(*ref) + len); - hashcpy(ref->new_sha1, sha1); - memcpy(ref->name, refname, len); - *local_tail = ref; - local_tail = &ref->next; - return 0; -} - static void one_remote_ref(char *refname) { struct ref *ref; @@ -1839,12 +1826,6 @@ static void one_remote_ref(char *refname) remote_tail = &ref->next; } -static void get_local_heads(void) -{ - local_tail = &local_refs; - for_each_ref(one_local_ref, NULL); -} - static void get_dav_remote_heads(void) { remote_tail = &remote_refs; @@ -2195,7 +2176,7 @@ int main(int argc, char **argv) int rc = 0; int i; int new_refs; - struct ref *ref; + struct ref *ref, *local_refs; char *rewritten_url = NULL; git_extract_argv0_path(argv[0]); @@ -2302,7 +2283,7 @@ int main(int argc, char **argv) fetch_indices(); /* Get a list of all local and remote heads to validate refspecs */ - get_local_heads(); + local_refs = get_local_heads(); fprintf(stderr, "Fetching remote heads...\n"); get_dav_remote_heads(); diff --git a/remote.c b/remote.c index d7079c6dd8..01aae770a9 100644 --- a/remote.c +++ b/remote.c @@ -1376,3 +1376,29 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb) base, num_ours, num_theirs); return 1; } + +static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) +{ + struct ref ***local_tail = cb_data; + struct ref *ref; + int len; + + /* we already know it starts with refs/ to get here */ + if (check_ref_format(refname + 5)) + return 0; + + len = strlen(refname) + 1; + ref = xcalloc(1, sizeof(*ref) + len); + hashcpy(ref->new_sha1, sha1); + memcpy(ref->name, refname, len); + **local_tail = ref; + *local_tail = &ref->next; + return 0; +} + +struct ref *get_local_heads(void) +{ + struct ref *local_refs, **local_tail = &local_refs; + for_each_ref(one_local_ref, &local_tail); + return local_refs; +} diff --git a/remote.h b/remote.h index a46a5be131..56ca8b168f 100644 --- a/remote.h +++ b/remote.h @@ -137,4 +137,5 @@ enum match_refs_flags { int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs); int format_tracking_info(struct branch *branch, struct strbuf *sb); +struct ref *get_local_heads(void); #endif From ec8452d5a797fca865666f761b785b04212426fc Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Wed, 25 Feb 2009 03:32:12 -0500 Subject: [PATCH 029/289] move duplicated ref_newer() to remote.c ref_newer() appears to have been copied from builtin-send-pack.c to http-push.c via cut and paste. This patch moves the function and its helper unmark_and_free() to remote.c. There was a slight difference between the two implementations, one used TMP_MARK for the mark, the other used 1. Per Jeff King, I went with TMP_MARK as more correct. This is in preparation for being able to call it from builtin-remote.c Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- builtin-send-pack.c | 50 --------------------------------------------- http-push.c | 49 -------------------------------------------- remote.c | 49 ++++++++++++++++++++++++++++++++++++++++++++ remote.h | 1 + 4 files changed, 50 insertions(+), 99 deletions(-) diff --git a/builtin-send-pack.c b/builtin-send-pack.c index 2fbfc291dc..9072905f10 100644 --- a/builtin-send-pack.c +++ b/builtin-send-pack.c @@ -1,6 +1,5 @@ #include "cache.h" #include "commit.h" -#include "tag.h" #include "refs.h" #include "pkt-line.h" #include "run-command.h" @@ -84,55 +83,6 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext return 0; } -static void unmark_and_free(struct commit_list *list, unsigned int mark) -{ - while (list) { - struct commit_list *temp = list; - temp->item->object.flags &= ~mark; - list = temp->next; - free(temp); - } -} - -static int ref_newer(const unsigned char *new_sha1, - const unsigned char *old_sha1) -{ - struct object *o; - struct commit *old, *new; - struct commit_list *list, *used; - int found = 0; - - /* Both new and old must be commit-ish and new is descendant of - * old. Otherwise we require --force. - */ - o = deref_tag(parse_object(old_sha1), NULL, 0); - if (!o || o->type != OBJ_COMMIT) - return 0; - old = (struct commit *) o; - - o = deref_tag(parse_object(new_sha1), NULL, 0); - if (!o || o->type != OBJ_COMMIT) - return 0; - new = (struct commit *) o; - - if (parse_commit(new) < 0) - return 0; - - used = list = NULL; - commit_list_insert(new, &list); - while (list) { - new = pop_most_recent_commit(&list, 1); - commit_list_insert(new, &used); - if (new == old) { - found = 1; - break; - } - } - unmark_and_free(list, 1); - unmark_and_free(used, 1); - return found; -} - static struct ref *remote_refs, **remote_tail; static int receive_status(int in, struct ref *refs) diff --git a/http-push.c b/http-push.c index cfeed81d07..392533a017 100644 --- a/http-push.c +++ b/http-push.c @@ -1843,55 +1843,6 @@ static int is_zero_sha1(const unsigned char *sha1) return 1; } -static void unmark_and_free(struct commit_list *list, unsigned int mark) -{ - while (list) { - struct commit_list *temp = list; - temp->item->object.flags &= ~mark; - list = temp->next; - free(temp); - } -} - -static int ref_newer(const unsigned char *new_sha1, - const unsigned char *old_sha1) -{ - struct object *o; - struct commit *old, *new; - struct commit_list *list, *used; - int found = 0; - - /* Both new and old must be commit-ish and new is descendant of - * old. Otherwise we require --force. - */ - o = deref_tag(parse_object(old_sha1), NULL, 0); - if (!o || o->type != OBJ_COMMIT) - return 0; - old = (struct commit *) o; - - o = deref_tag(parse_object(new_sha1), NULL, 0); - if (!o || o->type != OBJ_COMMIT) - return 0; - new = (struct commit *) o; - - if (parse_commit(new) < 0) - return 0; - - used = list = NULL; - commit_list_insert(new, &list); - while (list) { - new = pop_most_recent_commit(&list, TMP_MARK); - commit_list_insert(new, &used); - if (new == old) { - found = 1; - break; - } - } - unmark_and_free(list, TMP_MARK); - unmark_and_free(used, TMP_MARK); - return found; -} - static void add_remote_info_ref(struct remote_ls_ctx *ls) { struct strbuf *buf = (struct strbuf *)ls->userData; diff --git a/remote.c b/remote.c index 01aae770a9..c8b7ea4ffa 100644 --- a/remote.c +++ b/remote.c @@ -5,6 +5,7 @@ #include "diff.h" #include "revision.h" #include "dir.h" +#include "tag.h" static struct refspec s_tag_refspec = { 0, @@ -1269,6 +1270,54 @@ int resolve_remote_symref(struct ref *ref, struct ref *list) return 1; } +static void unmark_and_free(struct commit_list *list, unsigned int mark) +{ + while (list) { + struct commit_list *temp = list; + temp->item->object.flags &= ~mark; + list = temp->next; + free(temp); + } +} + +int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1) +{ + struct object *o; + struct commit *old, *new; + struct commit_list *list, *used; + int found = 0; + + /* Both new and old must be commit-ish and new is descendant of + * old. Otherwise we require --force. + */ + o = deref_tag(parse_object(old_sha1), NULL, 0); + if (!o || o->type != OBJ_COMMIT) + return 0; + old = (struct commit *) o; + + o = deref_tag(parse_object(new_sha1), NULL, 0); + if (!o || o->type != OBJ_COMMIT) + return 0; + new = (struct commit *) o; + + if (parse_commit(new) < 0) + return 0; + + used = list = NULL; + commit_list_insert(new, &list); + while (list) { + new = pop_most_recent_commit(&list, TMP_MARK); + commit_list_insert(new, &used); + if (new == old) { + found = 1; + break; + } + } + unmark_and_free(list, TMP_MARK); + unmark_and_free(used, TMP_MARK); + return found; +} + /* * Return true if there is anything to report, otherwise false. */ diff --git a/remote.h b/remote.h index 56ca8b168f..c0666a0758 100644 --- a/remote.h +++ b/remote.h @@ -74,6 +74,7 @@ int check_ref_type(const struct ref *ref, int flags); void free_refs(struct ref *ref); int resolve_remote_symref(struct ref *ref, struct ref *list); +int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1); /* * Removes and frees any duplicate refs in the map. From 8ef517337dc684a333111b46d88c3217202f48c3 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Wed, 25 Feb 2009 03:32:13 -0500 Subject: [PATCH 030/289] move locate_head() to remote.c Move locate_head() to remote.c and rename it to guess_remote_head() to more accurately reflect what it does. This is in preparation for being able to call it from builtin-remote.c Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- builtin-clone.c | 41 +++-------------------------------------- remote.c | 37 +++++++++++++++++++++++++++++++++++++ remote.h | 9 +++++++++ 3 files changed, 49 insertions(+), 38 deletions(-) diff --git a/builtin-clone.c b/builtin-clone.c index c338910b1c..d179d1c632 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -20,6 +20,7 @@ #include "dir.h" #include "pack-refs.h" #include "sigchain.h" +#include "remote.h" /* * Overall FIXMEs: @@ -293,43 +294,6 @@ static void remove_junk_on_signal(int signo) raise(signo); } -static const struct ref *locate_head(const struct ref *refs, - const struct ref *mapped_refs, - const struct ref **remote_head_p) -{ - const struct ref *remote_head = NULL; - const struct ref *remote_master = NULL; - const struct ref *r; - for (r = refs; r; r = r->next) - if (!strcmp(r->name, "HEAD")) - remote_head = r; - - for (r = mapped_refs; r; r = r->next) - if (!strcmp(r->name, "refs/heads/master")) - remote_master = r; - - if (remote_head_p) - *remote_head_p = remote_head; - - /* If there's no HEAD value at all, never mind. */ - if (!remote_head) - return NULL; - - /* If refs/heads/master could be right, it is. */ - if (remote_master && !hashcmp(remote_master->old_sha1, - remote_head->old_sha1)) - return remote_master; - - /* Look for another ref that points there */ - for (r = mapped_refs; r; r = r->next) - if (r != remote_head && - !hashcmp(r->old_sha1, remote_head->old_sha1)) - return r; - - /* Nothing is the same */ - return NULL; -} - static struct ref *write_remote_refs(const struct ref *refs, struct refspec *refspec, const char *reflog) { @@ -545,7 +509,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix) mapped_refs = write_remote_refs(refs, &refspec, reflog_msg.buf); - head_points_at = locate_head(refs, mapped_refs, &remote_head); + head_points_at = guess_remote_head(refs, mapped_refs, + &remote_head); } else { warning("You appear to have cloned an empty repository."); diff --git a/remote.c b/remote.c index c8b7ea4ffa..49a183eb5a 100644 --- a/remote.c +++ b/remote.c @@ -1451,3 +1451,40 @@ struct ref *get_local_heads(void) for_each_ref(one_local_ref, &local_tail); return local_refs; } + +const struct ref *guess_remote_head(const struct ref *refs, + const struct ref *mapped_refs, + const struct ref **remote_head_p) +{ + const struct ref *remote_head = NULL; + const struct ref *remote_master = NULL; + const struct ref *r; + for (r = refs; r; r = r->next) + if (!strcmp(r->name, "HEAD")) + remote_head = r; + + for (r = mapped_refs; r; r = r->next) + if (!strcmp(r->name, "refs/heads/master")) + remote_master = r; + + if (remote_head_p) + *remote_head_p = remote_head; + + /* If there's no HEAD value at all, never mind. */ + if (!remote_head) + return NULL; + + /* If refs/heads/master could be right, it is. */ + if (remote_master && !hashcmp(remote_master->old_sha1, + remote_head->old_sha1)) + return remote_master; + + /* Look for another ref that points there */ + for (r = mapped_refs; r; r = r->next) + if (r != remote_head && + !hashcmp(r->old_sha1, remote_head->old_sha1)) + return r; + + /* Nothing is the same */ + return NULL; +} diff --git a/remote.h b/remote.h index c0666a0758..9605da9e16 100644 --- a/remote.h +++ b/remote.h @@ -139,4 +139,13 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs); int format_tracking_info(struct branch *branch, struct strbuf *sb); struct ref *get_local_heads(void); +/* + * Look in refs for HEAD. Then look for a matching SHA1 in mapped_refs, + * first checking if refs/heads/master matches. Return NULL if nothing matches + * or if there is no HEAD in refs. remote_head_p is assigned HEAD if not NULL. + */ +const struct ref *guess_remote_head(const struct ref *refs, + const struct ref *mapped_refs, + const struct ref **remote_head_p); + #endif From 6cb4e6cc0f5b2de1998492b0178eeb0f99d4a800 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Wed, 25 Feb 2009 03:32:14 -0500 Subject: [PATCH 031/289] remote: simplify guess_remote_head() This function had complications which made it hard to extend. - It used to do two things: find the HEAD ref, and then find a matching ref, optionally returning the former via assignment to a passed-in pointer. Since finding HEAD is a one-liner, just have a caller do it themselves and pass it as an argument. - It used to manually search through the ref list for refs/heads/master; this can be a one-line call to find_ref_by_name. Originally contributed by Jeff King along with the next commit as a single patch. Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- builtin-clone.c | 4 ++-- remote.c | 31 ++++++++----------------------- remote.h | 15 +++++++-------- 3 files changed, 17 insertions(+), 33 deletions(-) diff --git a/builtin-clone.c b/builtin-clone.c index d179d1c632..f9ce4fbf19 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -509,8 +509,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix) mapped_refs = write_remote_refs(refs, &refspec, reflog_msg.buf); - head_points_at = guess_remote_head(refs, mapped_refs, - &remote_head); + remote_head = find_ref_by_name(refs, "HEAD"); + head_points_at = guess_remote_head(remote_head, mapped_refs); } else { warning("You appear to have cloned an empty repository."); diff --git a/remote.c b/remote.c index 49a183eb5a..aed760ee3a 100644 --- a/remote.c +++ b/remote.c @@ -1452,37 +1452,22 @@ struct ref *get_local_heads(void) return local_refs; } -const struct ref *guess_remote_head(const struct ref *refs, - const struct ref *mapped_refs, - const struct ref **remote_head_p) +const struct ref *guess_remote_head(const struct ref *head, + const struct ref *refs) { - const struct ref *remote_head = NULL; - const struct ref *remote_master = NULL; const struct ref *r; - for (r = refs; r; r = r->next) - if (!strcmp(r->name, "HEAD")) - remote_head = r; - for (r = mapped_refs; r; r = r->next) - if (!strcmp(r->name, "refs/heads/master")) - remote_master = r; - - if (remote_head_p) - *remote_head_p = remote_head; - - /* If there's no HEAD value at all, never mind. */ - if (!remote_head) + if (!head) return NULL; /* If refs/heads/master could be right, it is. */ - if (remote_master && !hashcmp(remote_master->old_sha1, - remote_head->old_sha1)) - return remote_master; + r = find_ref_by_name(refs, "refs/heads/master"); + if (r && !hashcmp(r->old_sha1, head->old_sha1)) + return r; /* Look for another ref that points there */ - for (r = mapped_refs; r; r = r->next) - if (r != remote_head && - !hashcmp(r->old_sha1, remote_head->old_sha1)) + for (r = refs; r; r = r->next) + if (r != head && !hashcmp(r->old_sha1, head->old_sha1)) return r; /* Nothing is the same */ diff --git a/remote.h b/remote.h index 9605da9e16..db49ce0467 100644 --- a/remote.h +++ b/remote.h @@ -139,13 +139,12 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs); int format_tracking_info(struct branch *branch, struct strbuf *sb); struct ref *get_local_heads(void); -/* - * Look in refs for HEAD. Then look for a matching SHA1 in mapped_refs, - * first checking if refs/heads/master matches. Return NULL if nothing matches - * or if there is no HEAD in refs. remote_head_p is assigned HEAD if not NULL. - */ -const struct ref *guess_remote_head(const struct ref *refs, - const struct ref *mapped_refs, - const struct ref **remote_head_p); +/* + * Look for a ref in refs whose SHA1 matches head, first checking if + * refs/heads/master matches. Return NULL if nothing matches or if head + * is NULL. + */ +const struct ref *guess_remote_head(const struct ref *head, + const struct ref *refs); #endif From 7b3db095d53d19e08b27114d8706ff3be6693af7 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Fri, 27 Feb 2009 14:10:04 -0500 Subject: [PATCH 032/289] remote: make copy_ref() perform a deep copy To ensure that copied refs can always be freed w/o causing a double-free, make copy_ref() perform a deep copy. Also have copy_ref() return NULL if asked to copy NULL to simplify things for the caller. Background: currently copy_ref() performs a shallow copy. This is fine for current callers who never free the result and/or only copy refs which contain NULL pointers. But copy_ref() is about to gain a new caller (guess_remote_head()) which copies refs where peer_ref is not NULL and the caller of guess_remote_head() will want to free the result. Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- remote.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/remote.c b/remote.c index aed760ee3a..22203ea8e2 100644 --- a/remote.c +++ b/remote.c @@ -779,10 +779,18 @@ struct ref *alloc_ref(const char *name) static struct ref *copy_ref(const struct ref *ref) { - struct ref *ret = xmalloc(sizeof(struct ref) + strlen(ref->name) + 1); - memcpy(ret, ref, sizeof(struct ref) + strlen(ref->name) + 1); - ret->next = NULL; - return ret; + struct ref *cpy; + size_t len; + if (!ref) + return NULL; + len = strlen(ref->name); + cpy = xmalloc(sizeof(struct ref) + len + 1); + memcpy(cpy, ref, sizeof(struct ref) + len + 1); + cpy->next = NULL; + cpy->symref = ref->symref ? xstrdup(ref->symref) : NULL; + cpy->remote_status = ref->remote_status ? xstrdup(ref->remote_status) : NULL; + cpy->peer_ref = copy_ref(ref->peer_ref); + return cpy; } struct ref *copy_ref_list(const struct ref *ref) @@ -801,6 +809,7 @@ static void free_ref(struct ref *ref) { if (!ref) return; + free_ref(ref->peer_ref); free(ref->remote_status); free(ref->symref); free(ref); @@ -811,7 +820,6 @@ void free_refs(struct ref *ref) struct ref *next; while (ref) { next = ref->next; - free(ref->peer_ref); free_ref(ref); ref = next; } From 4229f1fa325870d6b24fe2a4c7d2ed5f14c6f771 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Fri, 27 Feb 2009 14:10:05 -0500 Subject: [PATCH 033/289] remote: let guess_remote_head() optionally return all matches Determining HEAD is ambiguous since it is done by comparing SHA1s. In the case of multiple matches we return refs/heads/master if it matches, else we return the first match we encounter. builtin-remote needs all matches returned to it, so add a flag for it to request such. To be simple and consistent, the return value is now a copy (including peer_ref) of the matching refs. Originally contributed by Jeff King along with the prior commit as a single patch. Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- builtin-clone.c | 2 +- remote.c | 29 +++++++++++++++++++---------- remote.h | 14 ++++++++------ 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/builtin-clone.c b/builtin-clone.c index f9ce4fbf19..3146ca87f8 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -510,7 +510,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) mapped_refs = write_remote_refs(refs, &refspec, reflog_msg.buf); remote_head = find_ref_by_name(refs, "HEAD"); - head_points_at = guess_remote_head(remote_head, mapped_refs); + head_points_at = guess_remote_head(remote_head, mapped_refs, 0); } else { warning("You appear to have cloned an empty repository."); diff --git a/remote.c b/remote.c index 22203ea8e2..304e967e3c 100644 --- a/remote.c +++ b/remote.c @@ -1460,24 +1460,33 @@ struct ref *get_local_heads(void) return local_refs; } -const struct ref *guess_remote_head(const struct ref *head, - const struct ref *refs) +struct ref *guess_remote_head(const struct ref *head, + const struct ref *refs, + int all) { const struct ref *r; + struct ref *list = NULL; + struct ref **tail = &list; if (!head) return NULL; /* If refs/heads/master could be right, it is. */ - r = find_ref_by_name(refs, "refs/heads/master"); - if (r && !hashcmp(r->old_sha1, head->old_sha1)) - return r; + if (!all) { + r = find_ref_by_name(refs, "refs/heads/master"); + if (r && !hashcmp(r->old_sha1, head->old_sha1)) + return copy_ref(r); + } /* Look for another ref that points there */ - for (r = refs; r; r = r->next) - if (r != head && !hashcmp(r->old_sha1, head->old_sha1)) - return r; + for (r = refs; r; r = r->next) { + if (r != head && !hashcmp(r->old_sha1, head->old_sha1)) { + *tail = copy_ref(r); + tail = &((*tail)->next); + if (!all) + break; + } + } - /* Nothing is the same */ - return NULL; + return list; } diff --git a/remote.h b/remote.h index db49ce0467..de3d21b662 100644 --- a/remote.h +++ b/remote.h @@ -139,12 +139,14 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs); int format_tracking_info(struct branch *branch, struct strbuf *sb); struct ref *get_local_heads(void); - /* - * Look for a ref in refs whose SHA1 matches head, first checking if - * refs/heads/master matches. Return NULL if nothing matches or if head - * is NULL. + * Find refs from a list which are likely to be pointed to by the given HEAD + * ref. If 'all' is false, returns the most likely ref; otherwise, returns a + * list of all candidate refs. If no match is found (or 'head' is NULL), + * returns NULL. All returns are newly allocated and should be freed. */ -const struct ref *guess_remote_head(const struct ref *head, - const struct ref *refs); +struct ref *guess_remote_head(const struct ref *head, + const struct ref *refs, + int all); + #endif From cdf690e53b5f5af1ca8679b3f3e47ea198692c18 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Wed, 25 Feb 2009 03:32:16 -0500 Subject: [PATCH 034/289] remote: make match_refs() copy src ref before assigning to peer_ref In some instances, match_refs() sets the peer_ref field of refs in the dst list such that it points to a ref in the src list. This prevents callers from freeing both the src and dst lists, as doing so would cause a double-free since free_refs() frees the peer_ref. As well, the following configuration causes two refs in the dst list to have the same peer_ref, which can also lead to a double-free: push = refs/heads/master:refs/heads/backup push = refs/heads/master:refs/heads/master Existing callers of match_heads() call it only once and then terminate, w/o ever bothering to free the src or dst lists, so this is not currently a problem. This patch modifies match_refs() to first copy any refs it plucks from the src list before assigning them as a peer_ref. This allows builtin-remote, a future caller, to free the src and dst lists. Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- remote.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/remote.c b/remote.c index 304e967e3c..b7606acc47 100644 --- a/remote.c +++ b/remote.c @@ -936,6 +936,7 @@ static int match_explicit(struct ref *src, struct ref *dst, struct refspec *rs) { struct ref *matched_src, *matched_dst; + int copy_src; const char *dst_value = rs->dst; char *dst_guess; @@ -946,6 +947,7 @@ static int match_explicit(struct ref *src, struct ref *dst, matched_src = matched_dst = NULL; switch (count_refspec_match(rs->src, src, &matched_src)) { case 1: + copy_src = 1; break; case 0: /* The source could be in the get_sha1() format @@ -955,6 +957,7 @@ static int match_explicit(struct ref *src, struct ref *dst, matched_src = try_explicit_object_name(rs->src); if (!matched_src) return error("src refspec %s does not match any.", rs->src); + copy_src = 0; break; default: return error("src refspec %s matches more than one.", rs->src); @@ -1000,7 +1003,7 @@ static int match_explicit(struct ref *src, struct ref *dst, return error("dst ref %s receives from more than one src.", matched_dst->name); else { - matched_dst->peer_ref = matched_src; + matched_dst->peer_ref = copy_src ? copy_ref(matched_src) : matched_src; matched_dst->force = rs->force; } return 0; @@ -1108,7 +1111,7 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, dst_peer = make_linked_ref(dst_name, dst_tail); hashcpy(dst_peer->new_sha1, src->new_sha1); } - dst_peer->peer_ref = src; + dst_peer->peer_ref = copy_ref(src); dst_peer->force = pat->force; free_name: free(dst_name); From 5f48cb95aa0d7311623df76249a1c8a1962550f5 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Wed, 25 Feb 2009 03:32:17 -0500 Subject: [PATCH 035/289] remote: make match_refs() not short-circuit match_refs() returns non-zero if there is an error in match_explicit_refs(), without handling any remaining pattern ref specs. Its existing callers exit upon receiving non-zero, so a partial result is of no consequence to them; however a new caller, builtin-remote, is interested in the complete result even if there are errors in match_explicit_refs(). Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- remote.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/remote.c b/remote.c index b7606acc47..2123005d4b 100644 --- a/remote.c +++ b/remote.c @@ -1052,6 +1052,7 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, struct refspec *rs; int send_all = flags & MATCH_REFS_ALL; int send_mirror = flags & MATCH_REFS_MIRROR; + int errs; static const char *default_refspec[] = { ":", 0 }; if (!nr_refspec) { @@ -1059,8 +1060,7 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, refspec = default_refspec; } rs = parse_push_refspec(nr_refspec, (const char **) refspec); - if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec)) - return -1; + errs = match_explicit_refs(src, dst, dst_tail, rs, nr_refspec); /* pick the remainder */ for ( ; src; src = src->next) { @@ -1116,6 +1116,8 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, free_name: free(dst_name); } + if (errs) + return -1; return 0; } From c6f5a7a916b36fc9dd00bb6dce3b68260579abe1 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Wed, 25 Feb 2009 03:32:18 -0500 Subject: [PATCH 036/289] string-list: new for_each_string_list() function Add a convenience function for iterating over a string_list's items via a callback. Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- string-list.c | 10 ++++++++++ string-list.h | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/string-list.c b/string-list.c index 15e14cf47a..1ac536e638 100644 --- a/string-list.c +++ b/string-list.c @@ -92,6 +92,16 @@ struct string_list_item *string_list_lookup(const char *string, struct string_li return list->items + i; } +int for_each_string_list(string_list_each_func_t fn, + struct string_list *list, void *cb_data) +{ + int i, ret = 0; + for (i = 0; i < list->nr; i++) + if ((ret = fn(&list->items[i], cb_data))) + break; + return ret; +} + void string_list_clear(struct string_list *list, int free_util) { if (list->items) { diff --git a/string-list.h b/string-list.h index d32ba05202..14bbc477de 100644 --- a/string-list.h +++ b/string-list.h @@ -20,6 +20,11 @@ void string_list_clear(struct string_list *list, int free_util); typedef void (*string_list_clear_func_t)(void *p, const char *str); void string_list_clear_func(struct string_list *list, string_list_clear_func_t clearfunc); +/* Use this function to iterate over each item */ +typedef int (*string_list_each_func_t)(struct string_list_item *, void *); +int for_each_string_list(string_list_each_func_t, + struct string_list *list, void *cb_data); + /* Use these functions only on sorted lists: */ int string_list_has_string(const struct string_list *list, const char *string); int string_list_find_insert_index(const struct string_list *list, const char *string, From 88733235615b24198599b533f9d02578ef739536 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Wed, 25 Feb 2009 03:32:19 -0500 Subject: [PATCH 037/289] builtin-remote: refactor duplicated cleanup code This patch moves identical lines of code into a cleanup function. The function has two callers and is about to gain a third. Also removed a bogus NEEDSWORK comment per Daniel Barkalow: Actually, the comment is wrong; "remote" comes from remote_get(), which returns things from a cache in remote.c; there could be a remote_put() to let the code know that the caller is done with the object, but it wouldn't presently do anything. Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- builtin-remote.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/builtin-remote.c b/builtin-remote.c index ac69d37c8a..b89a3534de 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -632,6 +632,13 @@ static void show_list(const char *title, struct string_list *list, printf(" %s\n", list->items[i].string); } +static void free_remote_ref_states(struct ref_states *states) +{ + string_list_clear(&states->new, 0); + string_list_clear(&states->stale, 0); + string_list_clear(&states->tracked, 0); +} + static int get_remote_ref_states(const char *name, struct ref_states *states, int query) @@ -738,10 +745,7 @@ static int show(int argc, const char **argv) } } - /* NEEDSWORK: free remote */ - string_list_clear(&states.new, 0); - string_list_clear(&states.stale, 0); - string_list_clear(&states.tracked, 0); + free_remote_ref_states(&states); } return result; @@ -792,10 +796,7 @@ static int prune(int argc, const char **argv) warn_dangling_symref(dangling_msg, refname); } - /* NEEDSWORK: free remote */ - string_list_clear(&states.new, 0); - string_list_clear(&states.stale, 0); - string_list_clear(&states.tracked, 0); + free_remote_ref_states(&states); } return result; From 7b9a5e276cc685788386f1dcbd6a201f9f18da16 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Wed, 25 Feb 2009 03:32:20 -0500 Subject: [PATCH 038/289] builtin-remote: remove unused code in get_ref_states get_ref_states() populates the util pointer of the string_list_item's that it adds to states->new and states->tracked, but nothing ever uses the pointer, so we can get rid of the extra code. Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- builtin-remote.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/builtin-remote.c b/builtin-remote.c index b89a3534de..3e6dee4ad5 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -250,18 +250,11 @@ static int get_ref_states(const struct ref *ref, struct ref_states *states) states->new.strdup_strings = states->tracked.strdup_strings = 1; for (ref = fetch_map; ref; ref = ref->next) { - struct string_list *target = &states->tracked; unsigned char sha1[20]; - void *util = NULL; - if (!ref->peer_ref || read_ref(ref->peer_ref->name, sha1)) - target = &states->new; - else { - target = &states->tracked; - if (hashcmp(sha1, ref->new_sha1)) - util = &states; - } - string_list_append(abbrev_branch(ref->name), target)->util = util; + string_list_append(abbrev_branch(ref->name), &states->new); + else + string_list_append(abbrev_branch(ref->name), &states->tracked); } free_refs(fetch_map); From e0cc81e63c7bb603545c90e47d4c6398f6347dfb Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Wed, 25 Feb 2009 03:32:21 -0500 Subject: [PATCH 039/289] builtin-remote: rename variables and eliminate redundant function call - The variable name "remote" is used as both a "char *" and as a "struct remote *"; this is confusing, so rename the former to remote_name. - Consistently refer to the refs returned by transport_get_remote_refs() as remote_refs. - There is no need to call "sort_string_list(&branch_list)" as branch_list is populated via string_list_insert(), which maintains its order. Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- builtin-remote.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/builtin-remote.c b/builtin-remote.c index 3e6dee4ad5..fc02e5f34b 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -143,7 +143,7 @@ static int add(int argc, const char **argv) } struct branch_info { - char *remote; + char *remote_name; struct string_list merge; }; @@ -182,9 +182,9 @@ static int config_read_branches(const char *key, const char *value, void *cb) item->util = xcalloc(sizeof(struct branch_info), 1); info = item->util; if (type == REMOTE) { - if (info->remote) + if (info->remote_name) warning("more than one branch.%s", key); - info->remote = xstrdup(value); + info->remote_name = xstrdup(value); } else { char *space = strchr(value, ' '); value = abbrev_branch(value); @@ -206,7 +206,6 @@ static void read_branches(void) if (branch_list.nr) return; git_config(config_read_branches, NULL); - sort_string_list(&branch_list); } struct ref_states { @@ -238,13 +237,14 @@ static int handle_one_branch(const char *refname, return 0; } -static int get_ref_states(const struct ref *ref, struct ref_states *states) +static int get_ref_states(const struct ref *remote_refs, struct ref_states *states) { struct ref *fetch_map = NULL, **tail = &fetch_map; + struct ref *ref; int i; for (i = 0; i < states->remote->fetch_refspec_nr; i++) - if (get_fetch_map(ref, states->remote->fetch + i, &tail, 1)) + if (get_fetch_map(remote_refs, states->remote->fetch + i, &tail, 1)) die("Could not get fetch map for refspec %s", states->remote->fetch_refspec[i]); @@ -459,7 +459,7 @@ static int mv(int argc, const char **argv) for (i = 0; i < branch_list.nr; i++) { struct string_list_item *item = branch_list.items + i; struct branch_info *info = item->util; - if (info->remote && !strcmp(info->remote, rename.old)) { + if (info->remote_name && !strcmp(info->remote_name, rename.old)) { strbuf_reset(&buf); strbuf_addf(&buf, "branch.%s.remote", item->string); if (git_config_set(buf.buf, rename.new)) { @@ -569,7 +569,7 @@ static int rm(int argc, const char **argv) for (i = 0; i < branch_list.nr; i++) { struct string_list_item *item = branch_list.items + i; struct branch_info *info = item->util; - if (info->remote && !strcmp(info->remote, remote->name)) { + if (info->remote_name && !strcmp(info->remote_name, remote->name)) { const char *keys[] = { "remote", "merge", NULL }, **k; for (k = keys; *k; k++) { strbuf_reset(&buf); @@ -637,7 +637,7 @@ static int get_remote_ref_states(const char *name, int query) { struct transport *transport; - const struct ref *ref; + const struct ref *remote_refs; states->remote = remote_get(name); if (!states->remote) @@ -648,10 +648,10 @@ static int get_remote_ref_states(const char *name, if (query) { transport = transport_get(NULL, states->remote->url_nr > 0 ? states->remote->url[0] : NULL); - ref = transport_get_remote_refs(transport); + remote_refs = transport_get_remote_refs(transport); transport_disconnect(transport); - get_ref_states(ref, states); + get_ref_states(remote_refs, states); } return 0; @@ -701,7 +701,7 @@ static int show(int argc, const char **argv) struct branch_info *info = branch->util; int j; - if (!info->merge.nr || strcmp(*argv, info->remote)) + if (!info->merge.nr || strcmp(*argv, info->remote_name)) continue; printf(" Remote branch%s merged with 'git pull' " "while on branch %s\n ", From cca7c97e37719eaee1d31cdaf1e638d19ecd69e4 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Wed, 25 Feb 2009 03:32:22 -0500 Subject: [PATCH 040/289] builtin-remote: make get_remote_ref_states() always populate states.tracked When not querying the remote, show() was having to populate states.tracked itself. It makes more sense for get_remote_ref_states() to do this consistently. Since show() is the only caller of get_remote_ref_states() with query=0, this change does not affect other callers. Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- builtin-remote.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/builtin-remote.c b/builtin-remote.c index fc02e5f34b..1b5e8b6811 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -632,6 +632,20 @@ static void free_remote_ref_states(struct ref_states *states) string_list_clear(&states->tracked, 0); } +static int append_ref_to_tracked_list(const char *refname, + const unsigned char *sha1, int flags, void *cb_data) +{ + struct ref_states *states = cb_data; + struct refspec refspec; + + memset(&refspec, 0, sizeof(refspec)); + refspec.dst = (char *)refname; + if (!remote_find_tracking(states->remote, &refspec)) + string_list_append(abbrev_branch(refspec.src), &states->tracked); + + return 0; +} + static int get_remote_ref_states(const char *name, struct ref_states *states, int query) @@ -652,21 +666,8 @@ static int get_remote_ref_states(const char *name, transport_disconnect(transport); get_ref_states(remote_refs, states); - } - - return 0; -} - -static int append_ref_to_tracked_list(const char *refname, - const unsigned char *sha1, int flags, void *cb_data) -{ - struct ref_states *states = cb_data; - struct refspec refspec; - - memset(&refspec, 0, sizeof(refspec)); - refspec.dst = (char *)refname; - if (!remote_find_tracking(states->remote, &refspec)) - string_list_append(abbrev_branch(refspec.src), &states->tracked); + } else + for_each_ref(append_ref_to_tracked_list, states); return 0; } @@ -720,8 +721,6 @@ static int show(int argc, const char **argv) "prune')", &states.stale, ""); } - if (no_query) - for_each_ref(append_ref_to_tracked_list, &states); show_list(" Tracked remote branch%s", &states.tracked, ""); if (states.remote->push_refspec_nr) { From 3bd925636cd11400d1840b39d0d18b640f32bdd2 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Wed, 25 Feb 2009 03:32:23 -0500 Subject: [PATCH 041/289] builtin-remote: fix two inconsistencies in the output of "show " Remote and stale branches are emitted in alphabetical order, but new and tracked branches are not. So sort the latter to be consistent with the former. This also lets us use more efficient string_list_has_string() instead of unsorted_string_list_has_string(). "show " prunes symrefs, but "show -n" does not. Fix the latter to match the former. Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- builtin-remote.c | 15 ++++++++++----- t/t5505-remote.sh | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/builtin-remote.c b/builtin-remote.c index 1b5e8b6811..963be6df95 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -226,10 +226,8 @@ static int handle_one_branch(const char *refname, const char *name = abbrev_branch(refspec.src); /* symbolic refs pointing nowhere were handled already */ if ((flags & REF_ISSYMREF) || - unsorted_string_list_has_string(&states->tracked, - name) || - unsorted_string_list_has_string(&states->new, - name)) + string_list_has_string(&states->tracked, name) || + string_list_has_string(&states->new, name)) return 0; item = string_list_append(name, &states->stale); item->util = xstrdup(refname); @@ -258,6 +256,8 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat } free_refs(fetch_map); + sort_string_list(&states->new); + sort_string_list(&states->tracked); for_each_ref(handle_one_branch, states); sort_string_list(&states->stale); @@ -638,6 +638,9 @@ static int append_ref_to_tracked_list(const char *refname, struct ref_states *states = cb_data; struct refspec refspec; + if (flags & REF_ISSYMREF) + return 0; + memset(&refspec, 0, sizeof(refspec)); refspec.dst = (char *)refname; if (!remote_find_tracking(states->remote, &refspec)) @@ -666,8 +669,10 @@ static int get_remote_ref_states(const char *name, transport_disconnect(transport); get_ref_states(remote_refs, states); - } else + } else { for_each_ref(append_ref_to_tracked_list, states); + sort_string_list(&states->tracked); + } return 0; } diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index eb637184a0..a13d4b66d6 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -141,8 +141,8 @@ cat > test/expect << EOF New remote branch (next fetch will store in remotes/origin) master Tracked remote branches - side master + side Local branches pushed with 'git push' master:upstream +refs/tags/lastbackup From e61e0cc6b7061d7e791e1c9722b9c4a6d85d629c Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Wed, 25 Feb 2009 03:32:24 -0500 Subject: [PATCH 042/289] builtin-remote: teach show to display remote HEAD This is in preparation for teaching remote how to set refs/remotes//HEAD to match what HEAD is set to at , but is useful in its own right. Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- builtin-remote.c | 54 ++++++++++++++++++++++++++++++++++++++++++----- t/t5505-remote.sh | 12 +++++++++-- 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/builtin-remote.c b/builtin-remote.c index 963be6df95..4543cf0826 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -18,6 +18,9 @@ static const char * const builtin_remote_usage[] = { NULL }; +#define GET_REF_STATES (1<<0) +#define GET_HEAD_NAMES (1<<1) + static int verbose; static int show_all(void); @@ -210,7 +213,7 @@ static void read_branches(void) struct ref_states { struct remote *remote; - struct string_list new, stale, tracked; + struct string_list new, stale, tracked, heads; }; static int handle_one_branch(const char *refname, @@ -264,6 +267,28 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat return 0; } +static int get_head_names(const struct ref *remote_refs, struct ref_states *states) +{ + struct ref *ref, *matches; + struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map; + struct refspec refspec; + + refspec.force = 0; + refspec.pattern = 1; + refspec.src = refspec.dst = "refs/heads/"; + states->heads.strdup_strings = 1; + get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0); + matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"), + fetch_map, 1); + for(ref = matches; ref; ref = ref->next) + string_list_append(abbrev_branch(ref->name), &states->heads); + + free_refs(fetch_map); + free_refs(matches); + + return 0; +} + struct known_remote { struct known_remote *next; struct remote *remote; @@ -630,6 +655,7 @@ static void free_remote_ref_states(struct ref_states *states) string_list_clear(&states->new, 0); string_list_clear(&states->stale, 0); string_list_clear(&states->tracked, 0); + string_list_clear(&states->heads, 0); } static int append_ref_to_tracked_list(const char *refname, @@ -668,7 +694,10 @@ static int get_remote_ref_states(const char *name, remote_refs = transport_get_remote_refs(transport); transport_disconnect(transport); - get_ref_states(remote_refs, states); + if (query & GET_REF_STATES) + get_ref_states(remote_refs, states); + if (query & GET_HEAD_NAMES) + get_head_names(remote_refs, states); } else { for_each_ref(append_ref_to_tracked_list, states); sort_string_list(&states->tracked); @@ -679,7 +708,7 @@ static int get_remote_ref_states(const char *name, static int show(int argc, const char **argv) { - int no_query = 0, result = 0; + int no_query = 0, result = 0, query_flag = 0; struct option options[] = { OPT_GROUP("show specific options"), OPT_BOOLEAN('n', NULL, &no_query, "do not query remotes"), @@ -692,15 +721,30 @@ static int show(int argc, const char **argv) if (argc < 1) return show_all(); + if (!no_query) + query_flag = (GET_REF_STATES | GET_HEAD_NAMES); + memset(&states, 0, sizeof(states)); for (; argc; argc--, argv++) { int i; - get_remote_ref_states(*argv, &states, !no_query); + get_remote_ref_states(*argv, &states, query_flag); printf("* remote %s\n URL: %s\n", *argv, states.remote->url_nr > 0 ? states.remote->url[0] : "(no URL)"); + if (no_query) + printf(" HEAD branch: (not queried)\n"); + else if (!states.heads.nr) + printf(" HEAD branch: (unknown)\n"); + else if (states.heads.nr == 1) + printf(" HEAD branch: %s\n", states.heads.items[0].string); + else { + printf(" HEAD branch (remote HEAD is ambiguous," + " may be one of the following):\n"); + for (i = 0; i < states.heads.nr; i++) + printf(" %s\n", states.heads.items[i].string); + } for (i = 0; i < branch_list.nr; i++) { struct string_list_item *branch = branch_list.items + i; @@ -772,7 +816,7 @@ static int prune(int argc, const char **argv) for (; argc; argc--, argv++) { int i; - get_remote_ref_states(*argv, &states, 1); + get_remote_ref_states(*argv, &states, GET_REF_STATES); if (states.stale.nr) { printf("Pruning %s\n", *argv); diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index a13d4b66d6..91525c3f9c 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -136,6 +136,7 @@ EOF cat > test/expect << EOF * remote origin URL: $(pwd)/one + HEAD branch: master Remote branch merged with 'git pull' while on branch master master New remote branch (next fetch will store in remotes/origin) @@ -146,6 +147,11 @@ cat > test/expect << EOF Local branches pushed with 'git push' master:upstream +refs/tags/lastbackup +* remote two + URL: ../two + HEAD branch (remote HEAD is ambiguous, may be one of the following): + another + master EOF test_expect_success 'show' ' @@ -154,6 +160,7 @@ test_expect_success 'show' ' refs/heads/master:refs/heads/upstream && git fetch && git branch -d -r origin/master && + git config --add remote.two.url ../two && (cd ../one && echo 1 > file && test_tick && @@ -162,13 +169,14 @@ test_expect_success 'show' ' refs/heads/master:refs/heads/upstream && git config --add remote.origin.push \ +refs/tags/lastbackup && - git remote show origin > output && + git remote show origin two > output && test_cmp expect output) ' cat > test/expect << EOF * remote origin URL: $(pwd)/one + HEAD branch: (not queried) Remote branch merged with 'git pull' while on branch master master Tracked remote branches @@ -343,7 +351,7 @@ test_expect_success '"remote show" does not show symbolic refs' ' git clone one three && (cd three && git remote show origin > output && - ! grep HEAD < output && + ! grep "^ *HEAD$" < output && ! grep -i stale < output) ' From bc14fac825d9728c311aaa9d0aecf4960d4a3103 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Wed, 25 Feb 2009 03:32:25 -0500 Subject: [PATCH 043/289] builtin-remote: add set-head subcommand Provide a porcelain command for setting and deleting $GIT_DIR/remotes//HEAD. While we're at it, document what $GIT_DIR/remotes//HEAD is all about. Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- Documentation/git-remote.txt | 28 +++++++++++- builtin-remote.c | 62 ++++++++++++++++++++++++++ contrib/completion/git-completion.bash | 2 +- t/t5505-remote.sh | 40 +++++++++++++++++ 4 files changed, 129 insertions(+), 3 deletions(-) diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt index fad983e297..c9c0e6f932 100644 --- a/Documentation/git-remote.txt +++ b/Documentation/git-remote.txt @@ -13,6 +13,7 @@ SYNOPSIS 'git remote add' [-t ] [-m ] [-f] [--mirror] 'git remote rename' 'git remote rm' +'git remote set-head' [-a | -d | ] 'git remote show' [-n] 'git remote prune' [-n | --dry-run] 'git remote update' [group] @@ -53,8 +54,7 @@ is created. You can give more than one `-t ` to track multiple branches without grabbing all branches. + With `-m ` option, `$GIT_DIR/remotes//HEAD` is set -up to point at remote's `` branch instead of whatever -branch the `HEAD` at the remote repository actually points at. +up to point at remote's `` branch. See also the set-head command. + In mirror mode, enabled with `\--mirror`, the refs will not be stored in the 'refs/remotes/' namespace, but in 'refs/heads/'. This option @@ -76,6 +76,30 @@ the configuration file format. Remove the remote named . All remote tracking branches and configuration settings for the remote are removed. +'set-head':: + +Sets or deletes the default branch (`$GIT_DIR/remotes//HEAD`) for +the named remote. Having a default branch for a remote is not required, +but allows the name of the remote to be specified in lieu of a specific +branch. For example, if the default branch for `origin` is set to +`master`, then `origin` may be specified wherever you would normally +specify `origin/master`. ++ +With `-d`, `$GIT_DIR/remotes//HEAD` is deleted. ++ +With `-a`, the remote is queried to determine its `HEAD`, then +`$GIT_DIR/remotes//HEAD` is set to the same branch. e.g., if the remote +`HEAD` is pointed at `next`, "`git remote set-head origin -a`" will set +`$GIT_DIR/refs/remotes/origin/HEAD` to `refs/remotes/origin/next`. This will +only work if `refs/remotes/origin/next` already exists; if not it must be +fetched first. ++ +Use `` to set `$GIT_DIR/remotes//HEAD` explicitly. e.g., "git +remote set-head origin master" will set `$GIT_DIR/refs/remotes/origin/HEAD` to +`refs/remotes/origin/master`. This will only work if +`refs/remotes/origin/master` already exists; if not it must be fetched first. ++ + 'show':: Gives some information about the remote . diff --git a/builtin-remote.c b/builtin-remote.c index 4543cf0826..640e4dafbf 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -12,6 +12,7 @@ static const char * const builtin_remote_usage[] = { "git remote add [-t ] [-m ] [-f] [--mirror] ", "git remote rename ", "git remote rm ", + "git remote set-head [-a | -d | ]", "git remote show [-n] ", "git remote prune [-n | --dry-run] ", "git remote [-v | --verbose] update [group]", @@ -792,6 +793,65 @@ static int show(int argc, const char **argv) return result; } +static int set_head(int argc, const char **argv) +{ + int i, opt_a = 0, opt_d = 0, result = 0; + struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT; + char *head_name = NULL; + + struct option options[] = { + OPT_GROUP("set-head specific options"), + OPT_BOOLEAN('a', "auto", &opt_a, + "set refs/remotes//HEAD according to remote"), + OPT_BOOLEAN('d', "delete", &opt_d, + "delete refs/remotes//HEAD"), + OPT_END() + }; + argc = parse_options(argc, argv, options, builtin_remote_usage, 0); + if (argc) + strbuf_addf(&buf, "refs/remotes/%s/HEAD", argv[0]); + + if (!opt_a && !opt_d && argc == 2) { + head_name = xstrdup(argv[1]); + } else if (opt_a && !opt_d && argc == 1) { + struct ref_states states; + memset(&states, 0, sizeof(states)); + get_remote_ref_states(argv[0], &states, GET_HEAD_NAMES); + if (!states.heads.nr) + result |= error("Cannot determine remote HEAD"); + else if (states.heads.nr > 1) { + result |= error("Multiple remote HEAD branches. " + "Please choose one explicitly with:"); + for (i = 0; i < states.heads.nr; i++) + fprintf(stderr, " git remote set-head %s %s\n", + argv[0], states.heads.items[i].string); + } else + head_name = xstrdup(states.heads.items[0].string); + free_remote_ref_states(&states); + } else if (opt_d && !opt_a && argc == 1) { + if (delete_ref(buf.buf, NULL, REF_NODEREF)) + result |= error("Could not delete %s", buf.buf); + } else + usage_with_options(builtin_remote_usage, options); + + if (head_name) { + unsigned char sha1[20]; + strbuf_addf(&buf2, "refs/remotes/%s/%s", argv[0], head_name); + /* make sure it's valid */ + if (!resolve_ref(buf2.buf, sha1, 1, NULL)) + result |= error("Not a valid ref: %s", buf2.buf); + else if (create_symref(buf.buf, buf2.buf, "remote set-head")) + result |= error("Could not setup %s", buf.buf); + if (opt_a) + printf("%s/HEAD set to %s\n", argv[0], head_name); + free(head_name); + } + + strbuf_release(&buf); + strbuf_release(&buf2); + return result; +} + static int prune(int argc, const char **argv) { int dry_run = 0, result = 0; @@ -962,6 +1022,8 @@ int cmd_remote(int argc, const char **argv, const char *prefix) result = mv(argc, argv); else if (!strcmp(argv[0], "rm")) result = rm(argc, argv); + else if (!strcmp(argv[0], "set-head")) + result = set_head(argc, argv); else if (!strcmp(argv[0], "show")) result = show(argc, argv); else if (!strcmp(argv[0], "prune")) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 0a3092f646..15b938b902 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1443,7 +1443,7 @@ _git_config () _git_remote () { - local subcommands="add rename rm show prune update" + local subcommands="add rename rm show prune update set-head" local subcommand="$(__git_find_subcommand "$subcommands")" if [ -z "$subcommand" ]; then __gitcomp "$subcommands" diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index 91525c3f9c..de1d0fcf43 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -205,6 +205,46 @@ test_expect_success 'prune' ' test_must_fail git rev-parse refs/remotes/origin/side) ' +test_expect_success 'set-head --delete' ' + (cd test && + git symbolic-ref refs/remotes/origin/HEAD && + git remote set-head --delete origin && + test_must_fail git symbolic-ref refs/remotes/origin/HEAD) +' + +test_expect_success 'set-head --auto' ' + (cd test && + git remote set-head --auto origin && + echo refs/remotes/origin/master >expect && + git symbolic-ref refs/remotes/origin/HEAD >output && + test_cmp expect output + ) +' + +cat >test/expect <output 2>&1 && + test_cmp expect output) +' + +cat >test/expect <output && + git remote set-head origin master && + test_cmp expect output) +' + cat > test/expect << EOF Pruning origin URL: $(pwd)/one From fbb074c25352627f650d2ea528ed694e77bece0f Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 27 Feb 2009 14:10:06 -0500 Subject: [PATCH 044/289] remote: make guess_remote_head() use exact HEAD lookup if it is available Our usual method for determining the ref pointed to by HEAD is to compare HEAD's sha1 to the sha1 of all refs, trying to find a unique match. However, some transports actually get to look at HEAD directly; we should make use of that information when it is available. Currently, only http remotes support this feature. Signed-off-by: Jeff King Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- remote.c | 8 ++++++++ t/t5550-http-fetch.sh | 11 +++++++++++ 2 files changed, 19 insertions(+) diff --git a/remote.c b/remote.c index 2123005d4b..9b8522db35 100644 --- a/remote.c +++ b/remote.c @@ -1476,6 +1476,14 @@ struct ref *guess_remote_head(const struct ref *head, if (!head) return NULL; + /* + * Some transports support directly peeking at + * where HEAD points; if that is the case, then + * we don't have to guess. + */ + if (head->symref) + return copy_ref(find_ref_by_name(refs, head->symref)); + /* If refs/heads/master could be right, it is. */ if (!all) { r = find_ref_by_name(refs, "refs/heads/master"); diff --git a/t/t5550-http-fetch.sh b/t/t5550-http-fetch.sh index b6e6ec9607..05b1b62cb6 100755 --- a/t/t5550-http-fetch.sh +++ b/t/t5550-http-fetch.sh @@ -42,5 +42,16 @@ test_expect_success 'fetch changes via http' ' test_cmp file clone/file ' +test_expect_success 'http remote detects correct HEAD' ' + git push public master:other && + (cd clone && + git remote set-head origin -d && + git remote set-head origin -a && + git symbolic-ref refs/remotes/origin/HEAD > output && + echo refs/remotes/origin/master > expect && + test_cmp expect output + ) +' + stop_httpd test_done From 7ecbbf877c9a0716ccccd25609b01023eecd27c0 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Wed, 25 Feb 2009 03:32:27 -0500 Subject: [PATCH 045/289] builtin-remote: new show output style The existing output of "git remote show " is too verbose for the information it provides. This patch teaches it to provide more information in less space. The output for push refspecs is addressed in the next patch. Before the patch: $ git remote show origin * remote origin URL: git://git.kernel.org/pub/scm/git/git.git HEAD branch: master Remote branch merged with 'git pull' while on branch master master Remote branch merged with 'git pull' while on branch next next Remote branches merged with 'git pull' while on branch octopus foo bar baz frotz New remote branch (next fetch will store in remotes/origin) html Stale tracking branch (use 'git remote prune') bogus Tracked remote branches maint man master next pu todo After this patch: $ git remote show origin * remote origin URL: git://git.kernel.org/pub/scm/git/git.git HEAD branch: master Remote branches: bogus stale (use 'git remote prune' to remove) html new (next fetch will store in remotes/origin) maint tracked man tracked master tracked next tracked pu tracked todo tracked Local branches configured for 'git pull': master rebases onto remote master next rebases onto remote next octopus merges with remote foo and with remote bar and with remote baz and with remote frotz $ git remote show origin -n * remote origin URL: git://git.kernel.org/pub/scm/git/git.git HEAD branch: (not queried) Remote branches: (status not queried) bogus maint man master next pu todo Local branches configured for 'git pull': master rebases onto remote master next rebases onto remote next octopus merges with remote foo and with remote bar and with remote baz and with remote frotz Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- builtin-remote.c | 178 +++++++++++++++++++++++++++++++++++----------- t/t5505-remote.sh | 38 +++++----- 2 files changed, 157 insertions(+), 59 deletions(-) diff --git a/builtin-remote.c b/builtin-remote.c index 640e4dafbf..379826eed5 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -149,6 +149,7 @@ static int add(int argc, const char **argv) struct branch_info { char *remote_name; struct string_list merge; + int rebase; }; static struct string_list branch_list; @@ -165,10 +166,11 @@ static const char *abbrev_ref(const char *name, const char *prefix) static int config_read_branches(const char *key, const char *value, void *cb) { if (!prefixcmp(key, "branch.")) { + const char *orig_key = key; char *name; struct string_list_item *item; struct branch_info *info; - enum { REMOTE, MERGE } type; + enum { REMOTE, MERGE, REBASE } type; key += 7; if (!postfixcmp(key, ".remote")) { @@ -177,6 +179,9 @@ static int config_read_branches(const char *key, const char *value, void *cb) } else if (!postfixcmp(key, ".merge")) { name = xstrndup(key, strlen(key) - 6); type = MERGE; + } else if (!postfixcmp(key, ".rebase")) { + name = xstrndup(key, strlen(key) - 7); + type = REBASE; } else return 0; @@ -187,9 +192,9 @@ static int config_read_branches(const char *key, const char *value, void *cb) info = item->util; if (type == REMOTE) { if (info->remote_name) - warning("more than one branch.%s", key); + warning("more than one %s", orig_key); info->remote_name = xstrdup(value); - } else { + } else if (type == MERGE) { char *space = strchr(value, ' '); value = abbrev_branch(value); while (space) { @@ -200,7 +205,8 @@ static int config_read_branches(const char *key, const char *value, void *cb) space = strchr(value, ' '); } string_list_append(xstrdup(value), &info->merge); - } + } else + info->rebase = git_config_bool(orig_key, value); } return 0; } @@ -215,6 +221,7 @@ static void read_branches(void) struct ref_states { struct remote *remote; struct string_list new, stale, tracked, heads; + int queried; }; static int handle_one_branch(const char *refname, @@ -637,20 +644,6 @@ static int rm(int argc, const char **argv) return result; } -static void show_list(const char *title, struct string_list *list, - const char *extra_arg) -{ - int i; - - if (!list->nr) - return; - - printf(title, list->nr > 1 ? "es" : "", extra_arg); - printf("\n"); - for (i = 0; i < list->nr; i++) - printf(" %s\n", list->items[i].string); -} - static void free_remote_ref_states(struct ref_states *states) { string_list_clear(&states->new, 0); @@ -695,6 +688,7 @@ static int get_remote_ref_states(const char *name, remote_refs = transport_get_remote_refs(transport); transport_disconnect(transport); + states->queried = 1; if (query & GET_REF_STATES) get_ref_states(remote_refs, states); if (query & GET_HEAD_NAMES) @@ -707,6 +701,104 @@ static int get_remote_ref_states(const char *name, return 0; } +struct show_info { + struct string_list *list; + struct ref_states *states; + int width; + int any_rebase; +}; + +int add_remote_to_show_info(struct string_list_item *item, void *cb_data) +{ + struct show_info *info = cb_data; + int n = strlen(item->string); + if (n > info->width) + info->width = n; + string_list_insert(item->string, info->list); + return 0; +} + +int show_remote_info_item(struct string_list_item *item, void *cb_data) +{ + struct show_info *info = cb_data; + struct ref_states *states = info->states; + const char *name = item->string; + + if (states->queried) { + const char *fmt = "%s"; + const char *arg = ""; + if (string_list_has_string(&states->new, name)) { + fmt = " new (next fetch will store in remotes/%s)"; + arg = states->remote->name; + } else if (string_list_has_string(&states->tracked, name)) + arg = " tracked"; + else if (string_list_has_string(&states->stale, name)) + arg = " stale (use 'git remote prune' to remove)"; + else + arg = " ???"; + printf(" %-*s", info->width, name); + printf(fmt, arg); + printf("\n"); + } else + printf(" %s\n", name); + + return 0; +} + +int add_local_to_show_info(struct string_list_item *branch_item, void *cb_data) +{ + struct show_info *show_info = cb_data; + struct ref_states *states = show_info->states; + struct branch_info *branch_info = branch_item->util; + struct string_list_item *item; + int n; + + if (!branch_info->merge.nr || !branch_info->remote_name || + strcmp(states->remote->name, branch_info->remote_name)) + return 0; + if ((n = strlen(branch_item->string)) > show_info->width) + show_info->width = n; + if (branch_info->rebase) + show_info->any_rebase = 1; + + item = string_list_insert(branch_item->string, show_info->list); + item->util = branch_info; + + return 0; +} + +int show_local_info_item(struct string_list_item *item, void *cb_data) +{ + struct show_info *show_info = cb_data; + struct branch_info *branch_info = item->util; + struct string_list *merge = &branch_info->merge; + const char *also; + int i; + + if (branch_info->rebase && branch_info->merge.nr > 1) { + error("invalid branch.%s.merge; cannot rebase onto > 1 branch", + item->string); + return 0; + } + + printf(" %-*s ", show_info->width, item->string); + if (branch_info->rebase) { + printf("rebases onto remote %s\n", merge->items[0].string); + return 0; + } else if (show_info->any_rebase) { + printf(" merges with remote %s\n", merge->items[0].string); + also = " and with remote"; + } else { + printf("merges with remote %s\n", merge->items[0].string); + also = " and with remote"; + } + for (i = 1; i < merge->nr; i++) + printf(" %-*s %s %s\n", show_info->width, "", also, + merge->items[i].string); + + return 0; +} + static int show(int argc, const char **argv) { int no_query = 0, result = 0, query_flag = 0; @@ -716,6 +808,8 @@ static int show(int argc, const char **argv) OPT_END() }; struct ref_states states; + struct string_list info_list = { NULL, 0, 0, 0 }; + struct show_info info; argc = parse_options(argc, argv, options, builtin_remote_usage, 0); @@ -726,6 +820,9 @@ static int show(int argc, const char **argv) query_flag = (GET_REF_STATES | GET_HEAD_NAMES); memset(&states, 0, sizeof(states)); + memset(&info, 0, sizeof(info)); + info.states = &states; + info.list = &info_list; for (; argc; argc--, argv++) { int i; @@ -747,32 +844,29 @@ static int show(int argc, const char **argv) printf(" %s\n", states.heads.items[i].string); } - for (i = 0; i < branch_list.nr; i++) { - struct string_list_item *branch = branch_list.items + i; - struct branch_info *info = branch->util; - int j; + /* remote branch info */ + info.width = 0; + for_each_string_list(add_remote_to_show_info, &states.new, &info); + for_each_string_list(add_remote_to_show_info, &states.tracked, &info); + for_each_string_list(add_remote_to_show_info, &states.stale, &info); + if (info.list->nr) + printf(" Remote branch%s:%s\n", + info.list->nr > 1 ? "es" : "", + no_query ? " (status not queried)" : ""); + for_each_string_list(show_remote_info_item, info.list, &info); + string_list_clear(info.list, 0); - if (!info->merge.nr || strcmp(*argv, info->remote_name)) - continue; - printf(" Remote branch%s merged with 'git pull' " - "while on branch %s\n ", - info->merge.nr > 1 ? "es" : "", - branch->string); - for (j = 0; j < info->merge.nr; j++) - printf(" %s", info->merge.items[j].string); - printf("\n"); - } - - if (!no_query) { - show_list(" New remote branch%s (next fetch " - "will store in remotes/%s)", - &states.new, states.remote->name); - show_list(" Stale tracking branch%s (use 'git remote " - "prune')", &states.stale, ""); - } - - show_list(" Tracked remote branch%s", &states.tracked, ""); + /* git pull info */ + info.width = 0; + info.any_rebase = 0; + for_each_string_list(add_local_to_show_info, &branch_list, &info); + if (info.list->nr) + printf(" Local branch%s configured for 'git pull':\n", + info.list->nr > 1 ? "es" : ""); + for_each_string_list(show_local_info_item, info.list, &info); + string_list_clear(info.list, 0); + /* git push info */ if (states.remote->push_refspec_nr) { printf(" Local branch%s pushed with 'git push'\n", states.remote->push_refspec_nr > 1 ? diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index de1d0fcf43..69e241a0a5 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -28,7 +28,7 @@ tokens_match () { } check_remote_track () { - actual=$(git remote show "$1" | sed -e '1,/Tracked/d') && + actual=$(git remote show "$1" | sed -ne 's|^ \(.*\) tracked$|\1|p') shift && tokens_match "$*" "$actual" } @@ -137,13 +137,15 @@ cat > test/expect << EOF * remote origin URL: $(pwd)/one HEAD branch: master - Remote branch merged with 'git pull' while on branch master - master - New remote branch (next fetch will store in remotes/origin) - master - Tracked remote branches - master - side + Remote branches: + master new (next fetch will store in remotes/origin) + side tracked + Local branches configured for 'git pull': + master merges with remote master + octopus merges with remote topic-a + and with remote topic-b + and with remote topic-c + rebase rebases onto remote master Local branches pushed with 'git push' master:upstream +refs/tags/lastbackup @@ -156,20 +158,22 @@ EOF test_expect_success 'show' ' (cd test && - git config --add remote.origin.fetch \ - refs/heads/master:refs/heads/upstream && + git config --add remote.origin.fetch refs/heads/master:refs/heads/upstream && git fetch && + git branch --track octopus origin/master && + git branch --track rebase origin/master && git branch -d -r origin/master && git config --add remote.two.url ../two && + git config branch.rebase.rebase true && + git config branch.octopus.merge "topic-a topic-b topic-c" && (cd ../one && echo 1 > file && test_tick && git commit -m update file) && - git config remote.origin.push \ - refs/heads/master:refs/heads/upstream && - git config --add remote.origin.push \ - +refs/tags/lastbackup && + git config remote.origin.push refs/heads/master:refs/heads/upstream && + git config --add remote.origin.push +refs/tags/lastbackup && git remote show origin two > output && + git branch -d rebase octopus && test_cmp expect output) ' @@ -177,11 +181,11 @@ cat > test/expect << EOF * remote origin URL: $(pwd)/one HEAD branch: (not queried) - Remote branch merged with 'git pull' while on branch master - master - Tracked remote branches + Remote branches: (status not queried) master side + Local branch configured for 'git pull': + master merges with remote master Local branches pushed with 'git push' master:upstream +refs/tags/lastbackup From e5dcbfd9ab7028c464909f26f523b85c1de912a2 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Wed, 25 Feb 2009 03:32:28 -0500 Subject: [PATCH 046/289] builtin-remote: new show output style for push refspecs The existing output of "git remote show " with respect to push ref specs is basically just to show the raw refspec. This patch teaches the command to interpret the refspecs and show how each branch will be pushed to the destination. The output gives the user an idea of what "git push" should do if it is run w/o any arguments. Example new output: 1a. Typical output with no push refspec (i.e. matching branches only) $ git remote show origin * remote origin [...] Local refs configured for 'git push': master pushes to master (up to date) next pushes to next (local out of date) 1b. Same as above, w/o querying the remote: $ git remote show origin -n * remote origin [...] Local ref configured for 'git push' (status not queried): (matching) pushes to (matching) 2a. With a forcing refspec (+), and a new topic (something like push = refs/heads/*:refs/heads/*): $ git remote show origin * remote origin [...] Local refs configured for 'git push': master pushes to master (fast forwardable) new-topic pushes to new-topic (create) next pushes to next (local out of date) pu forces to pu (up to date) 2b. Same as above, w/o querying the remote $ git remote show origin -n * remote origin [...] Local refs configured for 'git push' (status not queried): master pushes to master new-topic pushes to new-topic next pushes to next pu forces to pu 3. With a remote configured as a mirror: * remote backup [...] Local refs will be mirrored by 'git push' Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- builtin-remote.c | 201 ++++++++++++++++++++++++++++++++++++++++++---- t/t5505-remote.sh | 30 +++++-- 2 files changed, 207 insertions(+), 24 deletions(-) diff --git a/builtin-remote.c b/builtin-remote.c index 379826eed5..7e82a52b7d 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -21,6 +21,7 @@ static const char * const builtin_remote_usage[] = { #define GET_REF_STATES (1<<0) #define GET_HEAD_NAMES (1<<1) +#define GET_PUSH_REF_STATES (1<<2) static int verbose; @@ -220,7 +221,7 @@ static void read_branches(void) struct ref_states { struct remote *remote; - struct string_list new, stale, tracked, heads; + struct string_list new, stale, tracked, heads, push; int queried; }; @@ -275,6 +276,112 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat return 0; } +struct push_info { + char *dest; + int forced; + enum { + PUSH_STATUS_CREATE = 0, + PUSH_STATUS_DELETE, + PUSH_STATUS_UPTODATE, + PUSH_STATUS_FASTFORWARD, + PUSH_STATUS_OUTOFDATE, + PUSH_STATUS_NOTQUERIED, + } status; +}; + +static int get_push_ref_states(const struct ref *remote_refs, + struct ref_states *states) +{ + struct remote *remote = states->remote; + struct ref *ref, *local_refs, *push_map, **push_tail; + if (remote->mirror) + return 0; + + local_refs = get_local_heads(); + ref = push_map = copy_ref_list(remote_refs); + while (ref->next) + ref = ref->next; + push_tail = &ref->next; + + match_refs(local_refs, push_map, &push_tail, remote->push_refspec_nr, + remote->push_refspec, MATCH_REFS_NONE); + + states->push.strdup_strings = 1; + for (ref = push_map; ref; ref = ref->next) { + struct string_list_item *item; + struct push_info *info; + + if (!ref->peer_ref) + continue; + hashcpy(ref->new_sha1, ref->peer_ref->new_sha1); + + item = string_list_append(abbrev_branch(ref->peer_ref->name), + &states->push); + item->util = xcalloc(sizeof(struct push_info), 1); + info = item->util; + info->forced = ref->force; + info->dest = xstrdup(abbrev_branch(ref->name)); + + if (is_null_sha1(ref->new_sha1)) { + info->status = PUSH_STATUS_DELETE; + } else if (!hashcmp(ref->old_sha1, ref->new_sha1)) + info->status = PUSH_STATUS_UPTODATE; + else if (is_null_sha1(ref->old_sha1)) + info->status = PUSH_STATUS_CREATE; + else if (has_sha1_file(ref->old_sha1) && + ref_newer(ref->new_sha1, ref->old_sha1)) + info->status = PUSH_STATUS_FASTFORWARD; + else + info->status = PUSH_STATUS_OUTOFDATE; + // ref->peer_ref = NULL; /* local ref which is freed below */ + } + free_refs(local_refs); + free_refs(push_map); + return 0; +} + +static int get_push_ref_states_noquery(struct ref_states *states) +{ + int i; + struct remote *remote = states->remote; + struct string_list_item *item; + struct push_info *info; + + if (remote->mirror) + return 0; + + states->push.strdup_strings = 1; + if (!remote->push_refspec_nr) { + item = string_list_append("(matching)", &states->push); + info = item->util = xcalloc(sizeof(struct push_info), 1); + info->status = PUSH_STATUS_NOTQUERIED; + info->dest = xstrdup(item->string); + } + for (i = 0; i < remote->push_refspec_nr; i++) { + struct refspec *spec = remote->push + i; + char buf[PATH_MAX]; + if (spec->matching) + item = string_list_append("(matching)", &states->push); + else if (spec->pattern) { + snprintf(buf, (sizeof(buf)), "%s*", spec->src); + item = string_list_append(buf, &states->push); + snprintf(buf, (sizeof(buf)), "%s*", spec->dst); + } else if (strlen(spec->src)) + item = string_list_append(spec->src, &states->push); + else + item = string_list_append("(delete)", &states->push); + + info = item->util = xcalloc(sizeof(struct push_info), 1); + info->forced = spec->force; + info->status = PUSH_STATUS_NOTQUERIED; + if (spec->pattern) + info->dest = xstrdup(buf); + else + info->dest = xstrdup(spec->dst ? spec->dst : item->string); + } + return 0; +} + static int get_head_names(const struct ref *remote_refs, struct ref_states *states) { struct ref *ref, *matches; @@ -644,12 +751,20 @@ static int rm(int argc, const char **argv) return result; } +void clear_push_info(void *util, const char *string) +{ + struct push_info *info = util; + free(info->dest); + free(info); +} + static void free_remote_ref_states(struct ref_states *states) { string_list_clear(&states->new, 0); string_list_clear(&states->stale, 0); string_list_clear(&states->tracked, 0); string_list_clear(&states->heads, 0); + string_list_clear_func(&states->push, clear_push_info); } static int append_ref_to_tracked_list(const char *refname, @@ -693,9 +808,12 @@ static int get_remote_ref_states(const char *name, get_ref_states(remote_refs, states); if (query & GET_HEAD_NAMES) get_head_names(remote_refs, states); + if (query & GET_PUSH_REF_STATES) + get_push_ref_states(remote_refs, states); } else { for_each_ref(append_ref_to_tracked_list, states); sort_string_list(&states->tracked); + get_push_ref_states_noquery(states); } return 0; @@ -704,7 +822,7 @@ static int get_remote_ref_states(const char *name, struct show_info { struct string_list *list; struct ref_states *states; - int width; + int width, width2; int any_rebase; }; @@ -799,6 +917,58 @@ int show_local_info_item(struct string_list_item *item, void *cb_data) return 0; } +int add_push_to_show_info(struct string_list_item *push_item, void *cb_data) +{ + struct show_info *show_info = cb_data; + struct push_info *push_info = push_item->util; + struct string_list_item *item; + int n; + if ((n = strlen(push_item->string)) > show_info->width) + show_info->width = n; + if ((n = strlen(push_info->dest)) > show_info->width2) + show_info->width2 = n; + item = string_list_append(push_item->string, show_info->list); + item->util = push_item->util; + return 0; +} + +int show_push_info_item(struct string_list_item *item, void *cb_data) +{ + struct show_info *show_info = cb_data; + struct push_info *push_info = item->util; + char *src = item->string, *status = NULL; + + switch (push_info->status) { + case PUSH_STATUS_CREATE: + status = "create"; + break; + case PUSH_STATUS_DELETE: + status = "delete"; + src = "(none)"; + break; + case PUSH_STATUS_UPTODATE: + status = "up to date"; + break; + case PUSH_STATUS_FASTFORWARD: + status = "fast forwardable"; + break; + case PUSH_STATUS_OUTOFDATE: + status = "local out of date"; + break; + case PUSH_STATUS_NOTQUERIED: + break; + } + if (status) + printf(" %-*s %s to %-*s (%s)\n", show_info->width, src, + push_info->forced ? "forces" : "pushes", + show_info->width2, push_info->dest, status); + else + printf(" %-*s %s to %s\n", show_info->width, src, + push_info->forced ? "forces" : "pushes", + push_info->dest); + return 0; +} + static int show(int argc, const char **argv) { int no_query = 0, result = 0, query_flag = 0; @@ -817,7 +987,7 @@ static int show(int argc, const char **argv) return show_all(); if (!no_query) - query_flag = (GET_REF_STATES | GET_HEAD_NAMES); + query_flag = (GET_REF_STATES | GET_HEAD_NAMES | GET_PUSH_REF_STATES); memset(&states, 0, sizeof(states)); memset(&info, 0, sizeof(info)); @@ -867,19 +1037,18 @@ static int show(int argc, const char **argv) string_list_clear(info.list, 0); /* git push info */ - if (states.remote->push_refspec_nr) { - printf(" Local branch%s pushed with 'git push'\n", - states.remote->push_refspec_nr > 1 ? - "es" : ""); - for (i = 0; i < states.remote->push_refspec_nr; i++) { - struct refspec *spec = states.remote->push + i; - printf(" %s%s%s%s\n", - spec->force ? "+" : "", - abbrev_branch(spec->src), - spec->dst ? ":" : "", - spec->dst ? abbrev_branch(spec->dst) : ""); - } - } + if (states.remote->mirror) + printf(" Local refs will be mirrored by 'git push'\n"); + + info.width = info.width2 = 0; + for_each_string_list(add_push_to_show_info, &states.push, &info); + sort_string_list(info.list); + if (info.list->nr) + printf(" Local ref%s configured for 'git push'%s:\n", + info.list->nr > 1 ? "s" : "", + no_query ? " (status not queried)" : ""); + for_each_string_list(show_push_info_item, info.list, &info); + string_list_clear(info.list, 0); free_remote_ref_states(&states); } diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index 69e241a0a5..5ec668d6d8 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -141,25 +141,34 @@ cat > test/expect << EOF master new (next fetch will store in remotes/origin) side tracked Local branches configured for 'git pull': + ahead merges with remote master master merges with remote master octopus merges with remote topic-a and with remote topic-b and with remote topic-c rebase rebases onto remote master - Local branches pushed with 'git push' - master:upstream - +refs/tags/lastbackup + Local refs configured for 'git push': + master pushes to master (local out of date) + master pushes to upstream (create) * remote two URL: ../two HEAD branch (remote HEAD is ambiguous, may be one of the following): another master + Local refs configured for 'git push': + ahead forces to master (fast forwardable) + master pushes to another (up to date) EOF test_expect_success 'show' ' (cd test && git config --add remote.origin.fetch refs/heads/master:refs/heads/upstream && git fetch && + git checkout -b ahead origin/master && + echo 1 >> file && + test_tick && + git commit -m update file && + git checkout master && git branch --track octopus origin/master && git branch --track rebase origin/master && git branch -d -r origin/master && @@ -170,8 +179,11 @@ test_expect_success 'show' ' echo 1 > file && test_tick && git commit -m update file) && - git config remote.origin.push refs/heads/master:refs/heads/upstream && + git config --add remote.origin.push : && + git config --add remote.origin.push refs/heads/master:refs/heads/upstream && git config --add remote.origin.push +refs/tags/lastbackup && + git config --add remote.two.push +refs/heads/ahead:refs/heads/master && + git config --add remote.two.push refs/heads/master:refs/heads/another && git remote show origin two > output && git branch -d rebase octopus && test_cmp expect output) @@ -184,11 +196,13 @@ cat > test/expect << EOF Remote branches: (status not queried) master side - Local branch configured for 'git pull': + Local branches configured for 'git pull': + ahead merges with remote master master merges with remote master - Local branches pushed with 'git push' - master:upstream - +refs/tags/lastbackup + Local refs configured for 'git push' (status not queried): + (matching) pushes to (matching) + refs/heads/master pushes to refs/heads/upstream + refs/tags/lastbackup forces to refs/tags/lastbackup EOF test_expect_success 'show -n' ' From 3e0c4ffdbddf1b3f84e0b8aa70e3b2fff68a56c5 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Sun, 1 Mar 2009 23:45:41 +0100 Subject: [PATCH 047/289] send-email: respect in-reply-to regardless of threading git-send-email supports the --in-reply-to option even with --no-thread. However, the code that adds the relevant mail headers was guarded by a test for --thread. Remove the test, so that the user's choice is respected. Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano --- git-send-email.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-send-email.perl b/git-send-email.perl index 449d938ba9..734dc9f4f9 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -727,7 +727,7 @@ Date: $date Message-Id: $message_id X-Mailer: git-send-email $gitversion "; - if ($thread && $reply_to) { + if ($reply_to) { $header .= "In-Reply-To: $reply_to\n"; $header .= "References: $references\n"; From 0e757e30c726d9d8ae82bd9989be3cff5d230288 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 3 Mar 2009 10:55:31 +0100 Subject: [PATCH 048/289] rebase -i: avoid 'git reset' when possible When picking commits whose parents have not changed, we do not need to rewrite the commit. We do not need to reset the working directory to the parent's state, either. Requested by Sverre Rabbelier. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- git-rebase--interactive.sh | 26 ++++++++++++++++++++++++++ t/t3404-rebase-interactive.sh | 11 +++++++++++ 2 files changed, 37 insertions(+) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 3dc659dd58..314cd364b8 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -442,6 +442,30 @@ do_rest () { done } +# skip picking commits whose parents are unchanged +skip_unnecessary_picks () { + fd=3 + while read command sha1 rest + do + # fd=3 means we skip the command + case "$fd,$command,$(git rev-parse --verify --quiet $sha1^)" in + 3,pick,"$ONTO"*|3,p,"$ONTO"*) + # pick a commit whose parent is current $ONTO -> skip + ONTO=$sha1 + ;; + 3,#*|3,,*) + # copy comments + ;; + *) + fd=1 + ;; + esac + echo "$command${sha1:+ }$sha1${rest:+ }$rest" >&$fd + done <"$TODO" >"$TODO.new" 3>>"$DONE" && + mv -f "$TODO".new "$TODO" || + die "Could not skip unnecessary pick commands" +} + # check if no other options are set is_standalone () { test $# -eq 2 -a "$2" = '--' && @@ -746,6 +770,8 @@ EOF has_action "$TODO" || die_abort "Nothing to do" + test -d "$REWRITTEN" || skip_unnecessary_picks + git update-ref ORIG_HEAD $HEAD output git checkout $ONTO && do_rest ;; diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 603b003edf..c32ff6682b 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -459,4 +459,15 @@ test_expect_success 'submodule rebase -i' ' FAKE_LINES="1 squash 2 3" git rebase -i A ' +test_expect_success 'avoid unnecessary reset' ' + git checkout master && + test-chmtime =123456789 file3 && + git update-index --refresh && + HEAD=$(git rev-parse HEAD) && + git rebase -i HEAD~4 && + test $HEAD = $(git rev-parse HEAD) && + MTIME=$(test-chmtime -v +0 file3 | sed 's/[^0-9].*$//') && + test 123456789 = $MTIME +' + test_done From a9f2c13685ae9040d52d53cd719a18040f1dd123 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 3 Mar 2009 22:29:55 -0800 Subject: [PATCH 049/289] Make git-clone respect branch.autosetuprebase When git-clone creates an initial branch it was not checking the branch.autosetuprebase configuration option (which may exist in ~/.gitconfig). Refactor the code used by "git branch" to create a new branch, and use it instead of the insufficiently duplicated code in builtin-clone. Changes are partly, and the test is mostly, based on the previous work by Pat Notz. Signed-off-by: Junio C Hamano --- branch.c | 49 ++++++++++++++++++++++++++++++++---------------- branch.h | 7 +++++++ builtin-clone.c | 18 +++--------------- t/t5601-clone.sh | 15 +++++++++++++++ 4 files changed, 58 insertions(+), 31 deletions(-) diff --git a/branch.c b/branch.c index 1f00e44deb..d20fb0490b 100644 --- a/branch.c +++ b/branch.c @@ -32,21 +32,48 @@ static int find_tracked_branch(struct remote *remote, void *priv) return 0; } -static int should_setup_rebase(const struct tracking *tracking) +static int should_setup_rebase(const char *origin) { switch (autorebase) { case AUTOREBASE_NEVER: return 0; case AUTOREBASE_LOCAL: - return tracking->remote == NULL; + return origin == NULL; case AUTOREBASE_REMOTE: - return tracking->remote != NULL; + return origin != NULL; case AUTOREBASE_ALWAYS: return 1; } return 0; } +void install_branch_config(int flag, const char *local, const char *origin, const char *remote) +{ + struct strbuf key = STRBUF_INIT; + int rebasing = should_setup_rebase(origin); + + strbuf_addf(&key, "branch.%s.remote", local); + git_config_set(key.buf, origin ? origin : "."); + + strbuf_reset(&key); + strbuf_addf(&key, "branch.%s.merge", local); + git_config_set(key.buf, remote); + + if (rebasing) { + strbuf_reset(&key); + strbuf_addf(&key, "branch.%s.rebase", local); + git_config_set(key.buf, "true"); + } + + if (flag & BRANCH_CONFIG_VERBOSE) + printf("Branch %s set up to track %s branch %s %s.\n", + local, + origin ? "remote" : "local", + remote, + rebasing ? "by rebasing" : "by merging"); + strbuf_release(&key); +} + /* * This is called when new_ref is branched off of orig_ref, and tries * to infer the settings for branch..{remote,merge} from the @@ -55,7 +82,6 @@ static int should_setup_rebase(const struct tracking *tracking) static int setup_tracking(const char *new_ref, const char *orig_ref, enum branch_track track) { - char key[1024]; struct tracking tracking; if (strlen(new_ref) > 1024 - 7 - 7 - 1) @@ -80,19 +106,10 @@ static int setup_tracking(const char *new_ref, const char *orig_ref, return error("Not tracking: ambiguous information for ref %s", orig_ref); - sprintf(key, "branch.%s.remote", new_ref); - git_config_set(key, tracking.remote ? tracking.remote : "."); - sprintf(key, "branch.%s.merge", new_ref); - git_config_set(key, tracking.src ? tracking.src : orig_ref); - printf("Branch %s set up to track %s branch %s.\n", new_ref, - tracking.remote ? "remote" : "local", orig_ref); - if (should_setup_rebase(&tracking)) { - sprintf(key, "branch.%s.rebase", new_ref); - git_config_set(key, "true"); - printf("This branch will rebase on pull.\n"); - } - free(tracking.src); + install_branch_config(BRANCH_CONFIG_VERBOSE, new_ref, tracking.remote, + tracking.src ? tracking.src : orig_ref); + free(tracking.src); return 0; } diff --git a/branch.h b/branch.h index 9f0c2a2c1f..eed817a64c 100644 --- a/branch.h +++ b/branch.h @@ -21,4 +21,11 @@ void create_branch(const char *head, const char *name, const char *start_name, */ void remove_branch_state(void); +/* + * Configure local branch "local" to merge remote branch "remote" + * taken from origin "origin". + */ +#define BRANCH_CONFIG_VERBOSE 01 +extern void install_branch_config(int flag, const char *local, const char *origin, const char *remote); + #endif diff --git a/builtin-clone.c b/builtin-clone.c index c338910b1c..a5f000adf4 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -20,6 +20,7 @@ #include "dir.h" #include "pack-refs.h" #include "sigchain.h" +#include "branch.h" /* * Overall FIXMEs: @@ -350,19 +351,6 @@ static struct ref *write_remote_refs(const struct ref *refs, return local_refs; } -static void install_branch_config(const char *local, - const char *origin, - const char *remote) -{ - struct strbuf key = STRBUF_INIT; - strbuf_addf(&key, "branch.%s.remote", local); - git_config_set(key.buf, origin); - strbuf_reset(&key); - strbuf_addf(&key, "branch.%s.merge", local); - git_config_set(key.buf, remote); - strbuf_release(&key); -} - int cmd_clone(int argc, const char **argv, const char *prefix) { int use_local_hardlinks = 1; @@ -553,7 +541,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) remote_head = NULL; option_no_checkout = 1; if (!option_bare) - install_branch_config("master", option_origin, + install_branch_config(0, "master", option_origin, "refs/heads/master"); } @@ -583,7 +571,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) head_points_at->peer_ref->name, reflog_msg.buf); - install_branch_config(head, option_origin, + install_branch_config(0, head, option_origin, head_points_at->name); } } else if (remote_head) { diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh index 44793f2eee..2335d8bc85 100755 --- a/t/t5601-clone.sh +++ b/t/t5601-clone.sh @@ -159,4 +159,19 @@ test_expect_success 'clone a void' ' test_cmp target-6/.git/config target-7/.git/config ' +test_expect_success 'clone respects global branch.autosetuprebase' ' + ( + HOME=$(pwd) && + export HOME && + test_config="$HOME/.gitconfig" && + unset GIT_CONFIG_NOGLOBAL && + git config -f "$test_config" branch.autosetuprebase remote && + rm -fr dst && + git clone src dst && + cd dst && + actual="z$(git config branch.master.rebase)" && + test ztrue = $actual + ) +' + test_done From 19de5d6913b9681d2bde533bccc8445c9236a648 Mon Sep 17 00:00:00 2001 From: Kjetil Barvik Date: Wed, 4 Mar 2009 18:47:39 +0100 Subject: [PATCH 050/289] stat_tracking_info(): only count real commits stat_tracking_info() in remote.c is used to collect the statistics to be able to say (for instance) from the output of "git checkout': Your branch and 'foo' have diverged, and have X and Y different commit(s) each, respectively. Currently X and Y also includes the count of merges. This patch excludes the merges from being counted. Signed-off-by: Kjetil Barvik Signed-off-by: Junio C Hamano --- remote.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/remote.c b/remote.c index d7079c6dd8..06c414e118 100644 --- a/remote.c +++ b/remote.c @@ -1310,9 +1310,10 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs) if (theirs == ours) return 0; - /* Run "rev-list --left-right ours...theirs" internally... */ + /* Run "rev-list --no-merges --left-right ours...theirs" internally... */ rev_argc = 0; rev_argv[rev_argc++] = NULL; + rev_argv[rev_argc++] = "--no-merges"; rev_argv[rev_argc++] = "--left-right"; rev_argv[rev_argc++] = symmetric; rev_argv[rev_argc++] = "--"; From ba048224685e661a4cf4736dcffab5fc60cbc70b Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sat, 7 Mar 2009 12:14:05 -0500 Subject: [PATCH 051/289] config: set help text for --bool-or-int The conversion to parse_opt left this as NULL; on glibc systems, the usage message prints --bool-or-int (null) and on other ones, segfaults. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin-config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-config.c b/builtin-config.c index b11a0961bd..1a3baa1f46 100644 --- a/builtin-config.c +++ b/builtin-config.c @@ -68,7 +68,7 @@ static struct option builtin_config_options[] = { OPT_GROUP("Type"), OPT_BIT(0, "bool", &types, "value is \"true\" or \"false\"", TYPE_BOOL), OPT_BIT(0, "int", &types, "value is decimal number", TYPE_INT), - OPT_BIT(0, "bool-or-int", &types, NULL, TYPE_BOOL_OR_INT), + OPT_BIT(0, "bool-or-int", &types, "value is --bool or --int", TYPE_BOOL_OR_INT), OPT_GROUP("Other"), OPT_BOOLEAN('z', "null", &end_null, "terminate values with NUL byte"), OPT_END(), From 252d560d215581637fcddd7a0a18f89204ecc8d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 7 Mar 2009 13:27:15 +0100 Subject: [PATCH 052/289] grep: micro-optimize hit collection for AND nodes In addition to returning if an expression matches a line, match_expr_eval() updates the expression's hit flag if the parameter collect_hits is set. It never sets collect_hits for children of AND nodes, though, so their hit flag will never be updated. Because of that we can return early if the first child didn't match, no matter if collect_hits is set or not. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- grep.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/grep.c b/grep.c index 062b2b6f28..db341b6797 100644 --- a/grep.c +++ b/grep.c @@ -394,13 +394,9 @@ static int match_expr_eval(struct grep_opt *o, h = !match_expr_eval(o, x->u.unary, bol, eol, ctx, 0); break; case GREP_NODE_AND: - if (!collect_hits) - return (match_expr_eval(o, x->u.binary.left, - bol, eol, ctx, 0) && - match_expr_eval(o, x->u.binary.right, - bol, eol, ctx, 0)); - h = match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0); - h &= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 0); + if (!match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0)) + return 0; + h = match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 0); break; case GREP_NODE_OR: if (!collect_hits) From d7eb527d731e2a71eaa4597417d879a15588d9ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 7 Mar 2009 13:28:40 +0100 Subject: [PATCH 053/289] grep: remove grep_opt argument from match_expr_eval() The only use of the struct grep_opt argument of match_expr_eval() is to pass the option word_regexp to match_one_pattern(). By adding a pattern flag for it we can reduce the number of function arguments of these two functions, as a cleanup and preparation for adding more in the next patch. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- grep.c | 34 +++++++++++++++++----------------- grep.h | 1 + 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/grep.c b/grep.c index db341b6797..f45518233f 100644 --- a/grep.c +++ b/grep.c @@ -39,6 +39,8 @@ static void compile_regexp(struct grep_pat *p, struct grep_opt *opt) { int err; + p->word_regexp = opt->word_regexp; + if (opt->fixed || is_fixed(p->pattern)) p->fixed = 1; if (opt->regflags & REG_ICASE) @@ -306,7 +308,8 @@ static struct { { "committer ", 10 }, }; -static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol, char *eol, enum grep_context ctx) +static int match_one_pattern(struct grep_pat *p, char *bol, char *eol, + enum grep_context ctx) { int hit = 0; int saved_ch = 0; @@ -338,7 +341,7 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol hit = !fixmatch(p->pattern, bol, pmatch); } - if (hit && opt->word_regexp) { + if (hit && p->word_regexp) { if ((pmatch[0].rm_so < 0) || (eol - bol) <= pmatch[0].rm_so || (pmatch[0].rm_eo < 0) || @@ -378,35 +381,32 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol return hit; } -static int match_expr_eval(struct grep_opt *o, - struct grep_expr *x, - char *bol, char *eol, - enum grep_context ctx, - int collect_hits) +static int match_expr_eval(struct grep_expr *x, char *bol, char *eol, + enum grep_context ctx, int collect_hits) { int h = 0; switch (x->node) { case GREP_NODE_ATOM: - h = match_one_pattern(o, x->u.atom, bol, eol, ctx); + h = match_one_pattern(x->u.atom, bol, eol, ctx); break; case GREP_NODE_NOT: - h = !match_expr_eval(o, x->u.unary, bol, eol, ctx, 0); + h = !match_expr_eval(x->u.unary, bol, eol, ctx, 0); break; case GREP_NODE_AND: - if (!match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0)) + if (!match_expr_eval(x->u.binary.left, bol, eol, ctx, 0)) return 0; - h = match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 0); + h = match_expr_eval(x->u.binary.right, bol, eol, ctx, 0); break; case GREP_NODE_OR: if (!collect_hits) - return (match_expr_eval(o, x->u.binary.left, + return (match_expr_eval(x->u.binary.left, bol, eol, ctx, 0) || - match_expr_eval(o, x->u.binary.right, + match_expr_eval(x->u.binary.right, bol, eol, ctx, 0)); - h = match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0); + h = match_expr_eval(x->u.binary.left, bol, eol, ctx, 0); x->u.binary.left->hit |= h; - h |= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 1); + h |= match_expr_eval(x->u.binary.right, bol, eol, ctx, 1); break; default: die("Unexpected node type (internal error) %d", x->node); @@ -420,7 +420,7 @@ static int match_expr(struct grep_opt *opt, char *bol, char *eol, enum grep_context ctx, int collect_hits) { struct grep_expr *x = opt->pattern_expression; - return match_expr_eval(opt, x, bol, eol, ctx, collect_hits); + return match_expr_eval(x, bol, eol, ctx, collect_hits); } static int match_line(struct grep_opt *opt, char *bol, char *eol, @@ -432,7 +432,7 @@ static int match_line(struct grep_opt *opt, char *bol, char *eol, /* we do not call with collect_hits without being extended */ for (p = opt->pattern_list; p; p = p->next) { - if (match_one_pattern(opt, p, bol, eol, ctx)) + if (match_one_pattern(p, bol, eol, ctx)) return 1; } return 0; diff --git a/grep.h b/grep.h index 5102ce335d..d2a8674be2 100644 --- a/grep.h +++ b/grep.h @@ -31,6 +31,7 @@ struct grep_pat { enum grep_header_field field; regex_t regexp; unsigned fixed:1; + unsigned word_regexp:1; }; enum grep_expr_node { From 79212772ce127cc32a67d50eaaa7f1c102ce7d5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 7 Mar 2009 13:30:27 +0100 Subject: [PATCH 054/289] grep: add pmatch and eflags arguments to match_one_pattern() Push pmatch and eflags to the callers of match_one_pattern(), which allows them to specify regex execution flags and to get the location of a match. Since we only use the first element of the matches array and aren't interested in submatches, no provision is made for callers to provide a larger array. eflags are ignored for fixed patterns, but that's OK, since they only have a meaning in connection with regular expressions containing ^ or $. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- grep.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/grep.c b/grep.c index f45518233f..bdcff7b9e4 100644 --- a/grep.c +++ b/grep.c @@ -309,11 +309,11 @@ static struct { }; static int match_one_pattern(struct grep_pat *p, char *bol, char *eol, - enum grep_context ctx) + enum grep_context ctx, + regmatch_t *pmatch, int eflags) { int hit = 0; int saved_ch = 0; - regmatch_t pmatch[10]; if ((p->token != GREP_PATTERN) && ((p->token == GREP_PATTERN_HEAD) != (ctx == GREP_CONTEXT_HEAD))) @@ -332,14 +332,10 @@ static int match_one_pattern(struct grep_pat *p, char *bol, char *eol, } again: - if (!p->fixed) { - regex_t *exp = &p->regexp; - hit = !regexec(exp, bol, ARRAY_SIZE(pmatch), - pmatch, 0); - } - else { + if (p->fixed) hit = !fixmatch(p->pattern, bol, pmatch); - } + else + hit = !regexec(&p->regexp, bol, 1, pmatch, eflags); if (hit && p->word_regexp) { if ((pmatch[0].rm_so < 0) || @@ -385,10 +381,11 @@ static int match_expr_eval(struct grep_expr *x, char *bol, char *eol, enum grep_context ctx, int collect_hits) { int h = 0; + regmatch_t match; switch (x->node) { case GREP_NODE_ATOM: - h = match_one_pattern(x->u.atom, bol, eol, ctx); + h = match_one_pattern(x->u.atom, bol, eol, ctx, &match, 0); break; case GREP_NODE_NOT: h = !match_expr_eval(x->u.unary, bol, eol, ctx, 0); @@ -427,12 +424,14 @@ static int match_line(struct grep_opt *opt, char *bol, char *eol, enum grep_context ctx, int collect_hits) { struct grep_pat *p; + regmatch_t match; + if (opt->extended) return match_expr(opt, bol, eol, ctx, collect_hits); /* we do not call with collect_hits without being extended */ for (p = opt->pattern_list; p; p = p->next) { - if (match_one_pattern(p, bol, eol, ctx)) + if (match_one_pattern(p, bol, eol, ctx, &match, 0)) return 1; } return 0; From 7e8f59d577e5615ceff06da0d9dde36a63608d53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 7 Mar 2009 13:32:32 +0100 Subject: [PATCH 055/289] grep: color patterns in output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coloring matches makes them easier to spot in the output. Add two options and two parameters: color.grep (to turn coloring on or off), color.grep.match (to set the color of matches), --color and --no-color (to turn coloring on or off, respectively). The output of external greps is not changed. This patch is based on earlier ones by Nguyễn Thái Ngọc Duy and Thiago Alves. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- Documentation/config.txt | 9 ++++ Documentation/git-grep.txt | 8 ++++ builtin-grep.c | 32 ++++++++++++++ grep.c | 90 +++++++++++++++++++++++++++++++++----- grep.h | 3 ++ 5 files changed, 130 insertions(+), 12 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index f5152c5038..b75dada9c3 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -548,6 +548,15 @@ color.diff.:: whitespace errors). The values of these variables may be specified as in color.branch.. +color.grep:: + When set to `always`, always highlight matches. When `false` (or + `never`), never. When set to `true` or `auto`, use color only + when the output is written to the terminal. Defaults to `false`. + +color.grep.match:: + Use customized color for matches. The value of this variable + may be specified as in color.branch.. + color.interactive:: When set to `always`, always use colors for interactive prompts and displays (such as those used by "git-add --interactive"). diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt index 553da6cbb1..fccb82deb4 100644 --- a/Documentation/git-grep.txt +++ b/Documentation/git-grep.txt @@ -17,6 +17,7 @@ SYNOPSIS [-l | --files-with-matches] [-L | --files-without-match] [-z | --null] [-c | --count] [--all-match] + [--color | --no-color] [-A ] [-B ] [-C ] [-f ] [-e] [--and|--or|--not|(|)|-e ...] [...] @@ -105,6 +106,13 @@ OPTIONS Instead of showing every matched line, show the number of lines that match. +--color:: + Show colored matches. + +--no-color:: + Turn off match highlighting, even when the configuration file + gives the default to color output. + -[ABC] :: Show `context` trailing (`A` -- after), or leading (`B` -- before), or both (`C` -- context) lines, and place a diff --git a/builtin-grep.c b/builtin-grep.c index 3f12ba3826..e2c0f01616 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -22,6 +22,24 @@ static int builtin_grep; +static int grep_config(const char *var, const char *value, void *cb) +{ + struct grep_opt *opt = cb; + + if (!strcmp(var, "grep.color") || !strcmp(var, "color.grep")) { + opt->color = git_config_colorbool(var, value, -1); + return 0; + } + if (!strcmp(var, "grep.color.match") || + !strcmp(var, "color.grep.match")) { + if (!value) + return config_error_nonbool(var); + color_parse(value, var, opt->color_match); + return 0; + } + return git_color_default_config(var, value, cb); +} + /* * git grep pathspecs are somewhat different from diff-tree pathspecs; * pathname wildcards are allowed. @@ -536,6 +554,12 @@ int cmd_grep(int argc, const char **argv, const char *prefix) opt.pattern_tail = &opt.pattern_list; opt.regflags = REG_NEWLINE; + strcpy(opt.color_match, GIT_COLOR_RED GIT_COLOR_BOLD); + opt.color = -1; + git_config(grep_config, &opt); + if (opt.color == -1) + opt.color = git_use_color_default; + /* * If there is no -- then the paths must exist in the working * tree. If there is no explicit pattern specified with -e or @@ -732,6 +756,14 @@ int cmd_grep(int argc, const char **argv, const char *prefix) opt.relative = 0; continue; } + if (!strcmp("--color", arg)) { + opt.color = 1; + continue; + } + if (!strcmp("--no-color", arg)) { + opt.color = 0; + continue; + } if (!strcmp("--", arg)) { /* later processing wants to have this at argv[1] */ argv--; diff --git a/grep.c b/grep.c index bdcff7b9e4..cace1c8bcb 100644 --- a/grep.c +++ b/grep.c @@ -253,18 +253,6 @@ static int word_char(char ch) return isalnum(ch) || ch == '_'; } -static void show_line(struct grep_opt *opt, const char *bol, const char *eol, - const char *name, unsigned lno, char sign) -{ - if (opt->null_following_name) - sign = '\0'; - if (opt->pathname) - printf("%s%c", name, sign); - if (opt->linenum) - printf("%d%c", lno, sign); - printf("%.*s\n", (int)(eol-bol), bol); -} - static void show_name(struct grep_opt *opt, const char *name) { printf("%s%c", name, opt->null_following_name ? '\0' : '\n'); @@ -437,6 +425,84 @@ static int match_line(struct grep_opt *opt, char *bol, char *eol, return 0; } +static int match_next_pattern(struct grep_pat *p, char *bol, char *eol, + enum grep_context ctx, + regmatch_t *pmatch, int eflags) +{ + regmatch_t match; + + if (!match_one_pattern(p, bol, eol, ctx, &match, eflags)) + return 0; + if (match.rm_so < 0 || match.rm_eo < 0) + return 0; + if (pmatch->rm_so >= 0 && pmatch->rm_eo >= 0) { + if (match.rm_so > pmatch->rm_so) + return 1; + if (match.rm_so == pmatch->rm_so && match.rm_eo < pmatch->rm_eo) + return 1; + } + pmatch->rm_so = match.rm_so; + pmatch->rm_eo = match.rm_eo; + return 1; +} + +static int next_match(struct grep_opt *opt, char *bol, char *eol, + enum grep_context ctx, regmatch_t *pmatch, int eflags) +{ + struct grep_pat *p; + int hit = 0; + + pmatch->rm_so = pmatch->rm_eo = -1; + if (bol < eol) { + for (p = opt->pattern_list; p; p = p->next) { + switch (p->token) { + case GREP_PATTERN: /* atom */ + case GREP_PATTERN_HEAD: + case GREP_PATTERN_BODY: + hit |= match_next_pattern(p, bol, eol, ctx, + pmatch, eflags); + break; + default: + break; + } + } + } + return hit; +} + +static void show_line(struct grep_opt *opt, char *bol, char *eol, + const char *name, unsigned lno, char sign) +{ + int rest = eol - bol; + + if (opt->null_following_name) + sign = '\0'; + if (opt->pathname) + printf("%s%c", name, sign); + if (opt->linenum) + printf("%d%c", lno, sign); + if (opt->color) { + regmatch_t match; + enum grep_context ctx = GREP_CONTEXT_BODY; + int ch = *eol; + int eflags = 0; + + *eol = '\0'; + while (next_match(opt, bol, eol, ctx, &match, eflags)) { + printf("%.*s%s%.*s%s", + match.rm_so, bol, + opt->color_match, + match.rm_eo - match.rm_so, bol + match.rm_so, + GIT_COLOR_RESET); + bol += match.rm_eo; + rest -= match.rm_eo; + eflags = REG_NOTBOL; + } + *eol = ch; + } + printf("%.*s\n", rest, bol); +} + static int grep_buffer_1(struct grep_opt *opt, const char *name, char *buf, unsigned long size, int collect_hits) { diff --git a/grep.h b/grep.h index d2a8674be2..73b33ab078 100644 --- a/grep.h +++ b/grep.h @@ -1,5 +1,6 @@ #ifndef GREP_H #define GREP_H +#include "color.h" enum grep_pat_token { GREP_PATTERN, @@ -77,6 +78,8 @@ struct grep_opt { unsigned relative:1; unsigned pathname:1; unsigned null_following_name:1; + int color; + char color_match[COLOR_MAXLEN]; int regflags; unsigned pre_context; unsigned post_context; From a94982ef39e1bb9a6f782b5b6ced22e97d6859b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 7 Mar 2009 13:34:46 +0100 Subject: [PATCH 056/289] grep: add support for coloring with external greps Add the config variable color.grep.external, which can be used to switch on coloring of external greps. To enable auto coloring with GNU grep, one needs to set color.grep.external to --color=always to defeat the pager started by git grep. The value of the config variable will be passed to the external grep only if it would colorize internal grep's output, so automatic terminal detected works. The default is to not pass any option, because the external grep command could be a program without color support. Also set the environment variables GREP_COLOR and GREP_COLORS to pass the configured color for matches to the external grep. This works with GNU grep; other variables could be added as needed. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- Documentation/config.txt | 12 +++++++++++- builtin-grep.c | 36 ++++++++++++++++++++++++++++++++++++ grep.h | 1 + 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index b75dada9c3..4d42bff718 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -553,9 +553,19 @@ color.grep:: `never`), never. When set to `true` or `auto`, use color only when the output is written to the terminal. Defaults to `false`. +color.grep.external:: + The string value of this variable is passed to an external 'grep' + command as a command line option if match highlighting is turned + on. If set to an empty string, no option is passed at all, + turning off coloring for external 'grep' calls; this is the default. + For GNU grep, set it to `--color=always` to highlight matches even + when a pager is used. + color.grep.match:: Use customized color for matches. The value of this variable - may be specified as in color.branch.. + may be specified as in color.branch.. It is passed using + the environment variables 'GREP_COLOR' and 'GREP_COLORS' when + calling an external 'grep'. color.interactive:: When set to `always`, always use colors for interactive prompts diff --git a/builtin-grep.c b/builtin-grep.c index e2c0f01616..9e7e766a49 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -30,6 +30,10 @@ static int grep_config(const char *var, const char *value, void *cb) opt->color = git_config_colorbool(var, value, -1); return 0; } + if (!strcmp(var, "grep.color.external") || + !strcmp(var, "color.grep.external")) { + return git_config_string(&(opt->color_external), var, value); + } if (!strcmp(var, "grep.color.match") || !strcmp(var, "color.grep.match")) { if (!value) @@ -287,6 +291,21 @@ static int flush_grep(struct grep_opt *opt, return status; } +static void grep_add_color(struct strbuf *sb, const char *escape_seq) +{ + size_t orig_len = sb->len; + + while (*escape_seq) { + if (*escape_seq == 'm') + strbuf_addch(sb, ';'); + else if (*escape_seq != '\033' && *escape_seq != '[') + strbuf_addch(sb, *escape_seq); + escape_seq++; + } + if (sb->len > orig_len && sb->buf[sb->len - 1] == ';') + strbuf_setlen(sb, sb->len - 1); +} + static int external_grep(struct grep_opt *opt, const char **paths, int cached) { int i, nr, argc, hit, len, status; @@ -357,6 +376,23 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) push_arg("-e"); push_arg(p->pattern); } + if (opt->color) { + struct strbuf sb = STRBUF_INIT; + + grep_add_color(&sb, opt->color_match); + setenv("GREP_COLOR", sb.buf, 1); + + strbuf_reset(&sb); + strbuf_addstr(&sb, "mt="); + grep_add_color(&sb, opt->color_match); + strbuf_addstr(&sb, ":sl=:cx=:fn=:ln=:bn=:se="); + setenv("GREP_COLORS", sb.buf, 1); + + strbuf_release(&sb); + + if (opt->color_external && strlen(opt->color_external) > 0) + push_arg(opt->color_external); + } hit = 0; argc = nr; diff --git a/grep.h b/grep.h index 73b33ab078..a67005de62 100644 --- a/grep.h +++ b/grep.h @@ -80,6 +80,7 @@ struct grep_opt { unsigned null_following_name:1; int color; char color_match[COLOR_MAXLEN]; + const char *color_external; int regflags; unsigned pre_context; unsigned post_context; From 689f03964360114bc1139d82b1f6a0d7b897bbf0 Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Thu, 5 Mar 2009 23:56:16 -0500 Subject: [PATCH 057/289] Make clone parse the default refspec with the normal code Instead of creating a refspec by hand, go through the refspec parsing code, so that changes in the refspec storage will be accounted for. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- builtin-clone.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/builtin-clone.c b/builtin-clone.c index c338910b1c..06b5a7fc39 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -378,7 +378,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix) struct transport *transport = NULL; char *src_ref_prefix = "refs/heads/"; - struct refspec refspec; + struct refspec *refspec; + const char *fetch_pattern; junk_pid = getpid(); @@ -487,8 +488,14 @@ int cmd_clone(int argc, const char **argv, const char *prefix) strbuf_addf(&branch_top, "refs/remotes/%s/", option_origin); } + strbuf_addf(&value, "+%s*:%s*", src_ref_prefix, branch_top.buf); + if (option_mirror || !option_bare) { /* Configure the remote */ + strbuf_addf(&key, "remote.%s.fetch", option_origin); + git_config_set_multivar(key.buf, value.buf, "^$", 0); + strbuf_reset(&key); + if (option_mirror) { strbuf_addf(&key, "remote.%s.mirror", option_origin); git_config_set(key.buf, "true"); @@ -497,19 +504,13 @@ int cmd_clone(int argc, const char **argv, const char *prefix) strbuf_addf(&key, "remote.%s.url", option_origin); git_config_set(key.buf, repo); - strbuf_reset(&key); - - strbuf_addf(&key, "remote.%s.fetch", option_origin); - strbuf_addf(&value, "+%s*:%s*", src_ref_prefix, branch_top.buf); - git_config_set_multivar(key.buf, value.buf, "^$", 0); strbuf_reset(&key); - strbuf_reset(&value); } - refspec.force = 0; - refspec.pattern = 1; - refspec.src = src_ref_prefix; - refspec.dst = branch_top.buf; + fetch_pattern = value.buf; + refspec = parse_fetch_refspec(1, &fetch_pattern); + + strbuf_reset(&value); if (path && !is_bundle) refs = clone_local(path, git_dir); @@ -543,7 +544,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (refs) { clear_extra_refs(); - mapped_refs = write_remote_refs(refs, &refspec, reflog_msg.buf); + mapped_refs = write_remote_refs(refs, refspec, reflog_msg.buf); head_points_at = locate_head(refs, mapped_refs, &remote_head); } From a3c8423901ec4996369b137b46354ddf1cfc0e0e Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Sat, 7 Mar 2009 01:11:29 -0500 Subject: [PATCH 058/289] Use a single function to match names against patterns This will help when the matching changes. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- remote.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/remote.c b/remote.c index d7079c6dd8..2816723bb9 100644 --- a/remote.c +++ b/remote.c @@ -719,6 +719,12 @@ int remote_has_url(struct remote *remote, const char *url) return 0; } +static int match_name_with_pattern(const char *key, const char *name) +{ + int ret = !prefixcmp(key, name); + return ret; +} + int remote_find_tracking(struct remote *remote, struct refspec *refspec) { int find_src = refspec->src == NULL; @@ -742,7 +748,7 @@ int remote_find_tracking(struct remote *remote, struct refspec *refspec) if (!fetch->dst) continue; if (fetch->pattern) { - if (!prefixcmp(needle, key)) { + if (match_name_with_pattern(key, needle)) { *result = xmalloc(strlen(value) + strlen(needle) - strlen(key) + 1); @@ -1020,7 +1026,7 @@ static const struct refspec *check_pattern_match(const struct refspec *rs, continue; } - if (rs[i].pattern && !prefixcmp(src->name, rs[i].src)) + if (rs[i].pattern && match_name_with_pattern(rs[i].src, src->name)) return rs + i; } if (matching_refs != -1) @@ -1160,7 +1166,7 @@ static struct ref *get_expanded_map(const struct ref *remote_refs, for (ref = remote_refs; ref; ref = ref->next) { if (strchr(ref->name, '^')) continue; /* a dereference item */ - if (!prefixcmp(ref->name, refspec->src)) { + if (match_name_with_pattern(refspec->src, ref->name)) { const char *match; struct ref *cpy = copy_ref(ref); match = ref->name + remote_prefix_len; From e928213fb40c106650dca2632b5e830cfaffb86a Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Sat, 7 Mar 2009 01:11:34 -0500 Subject: [PATCH 059/289] Use the matching function to generate the match results This puts all of the interpretation of the pattern representation in a single function for easy manipulation. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- remote.c | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/remote.c b/remote.c index 2816723bb9..01b8f91c5b 100644 --- a/remote.c +++ b/remote.c @@ -719,9 +719,19 @@ int remote_has_url(struct remote *remote, const char *url) return 0; } -static int match_name_with_pattern(const char *key, const char *name) +static int match_name_with_pattern(const char *key, const char *name, + const char *value, char **result) { - int ret = !prefixcmp(key, name); + size_t klen = strlen(key); + int ret = !strncmp(key, name, klen); + if (ret && value) { + size_t vlen = strlen(value); + *result = xmalloc(vlen + + strlen(name) - + klen + 1); + strcpy(*result, value); + strcpy(*result + vlen, name + klen); + } return ret; } @@ -748,13 +758,7 @@ int remote_find_tracking(struct remote *remote, struct refspec *refspec) if (!fetch->dst) continue; if (fetch->pattern) { - if (match_name_with_pattern(key, needle)) { - *result = xmalloc(strlen(value) + - strlen(needle) - - strlen(key) + 1); - strcpy(*result, value); - strcpy(*result + strlen(value), - needle + strlen(key)); + if (match_name_with_pattern(key, needle, value, result)) { refspec->force = fetch->force; return 0; } @@ -1026,7 +1030,8 @@ static const struct refspec *check_pattern_match(const struct refspec *rs, continue; } - if (rs[i].pattern && match_name_with_pattern(rs[i].src, src->name)) + if (rs[i].pattern && match_name_with_pattern(rs[i].src, src->name, + NULL, NULL)) return rs + i; } if (matching_refs != -1) @@ -1080,11 +1085,9 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, } else { const char *dst_side = pat->dst ? pat->dst : pat->src; - dst_name = xmalloc(strlen(dst_side) + - strlen(src->name) - - strlen(pat->src) + 2); - strcpy(dst_name, dst_side); - strcat(dst_name, src->name + strlen(pat->src)); + if (!match_name_with_pattern(pat->src, src->name, + dst_side, &dst_name)) + die("Didn't think it matches any more"); } dst_peer = find_ref_by_name(dst, dst_name); if (dst_peer) { @@ -1160,19 +1163,17 @@ static struct ref *get_expanded_map(const struct ref *remote_refs, struct ref *ret = NULL; struct ref **tail = &ret; - int remote_prefix_len = strlen(refspec->src); - int local_prefix_len = strlen(refspec->dst); + char *expn_name; for (ref = remote_refs; ref; ref = ref->next) { if (strchr(ref->name, '^')) continue; /* a dereference item */ - if (match_name_with_pattern(refspec->src, ref->name)) { - const char *match; + if (match_name_with_pattern(refspec->src, ref->name, + refspec->dst, &expn_name)) { struct ref *cpy = copy_ref(ref); - match = ref->name + remote_prefix_len; - cpy->peer_ref = alloc_ref_with_prefix(refspec->dst, - local_prefix_len, match); + cpy->peer_ref = alloc_ref(expn_name); + free(expn_name); if (refspec->force) cpy->peer_ref->force = 1; *tail = cpy; From 08fbdb30438fd7087c5abe15840a22fe21094515 Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Sat, 7 Mar 2009 01:11:36 -0500 Subject: [PATCH 060/289] Keep '*' in pattern refspecs In order to do anything more capable with refspecs, the first step is to keep the entire input. Additionally, validate patterns by checking for the ref matching the rules for a pattern as given by check_ref_format(). This requires a slight change to check_ref_format() to make it enforce the requirement that the '*' immediately follow a '/'. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- refs.c | 4 +--- remote.c | 33 ++++++++++++++++++--------------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/refs.c b/refs.c index 6eb5f53846..a50ba79270 100644 --- a/refs.c +++ b/refs.c @@ -718,9 +718,7 @@ int check_ref_format(const char *ref) while ((ch = *cp++) != 0) { bad_type = bad_ref_char(ch); if (bad_type) { - return (bad_type == 2 && !*cp) - ? CHECK_REF_FORMAT_WILDCARD - : CHECK_REF_FORMAT_ERROR; + return CHECK_REF_FORMAT_ERROR; } if (ch == '/') break; diff --git a/remote.c b/remote.c index 01b8f91c5b..d596a48651 100644 --- a/remote.c +++ b/remote.c @@ -10,8 +10,8 @@ static struct refspec s_tag_refspec = { 0, 1, 0, - "refs/tags/", - "refs/tags/" + "refs/tags/*", + "refs/tags/*" }; const struct refspec *tag_refspec = &s_tag_refspec; @@ -451,16 +451,11 @@ static void read_config(void) */ static int verify_refname(char *name, int is_glob) { - int result, len = -1; + int result; - if (is_glob) { - len = strlen(name); - assert(name[len - 1] == '/'); - name[len - 1] = '\0'; - } result = check_ref_format(name); - if (is_glob) - name[len - 1] = '/'; + if (is_glob && result == CHECK_REF_FORMAT_WILDCARD) + result = CHECK_REF_FORMAT_OK; return result; } @@ -517,7 +512,7 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp if (rhs) { size_t rlen = strlen(++rhs); is_glob = (2 <= rlen && !strcmp(rhs + rlen - 2, "/*")); - rs[i].dst = xstrndup(rhs, rlen - is_glob); + rs[i].dst = xstrndup(rhs, rlen); } llen = (rhs ? (rhs - lhs - 1) : strlen(lhs)); @@ -525,7 +520,6 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp if ((rhs && !is_glob) || (!rhs && fetch)) goto invalid; is_glob = 1; - llen--; } else if (rhs && is_glob) { goto invalid; } @@ -722,10 +716,19 @@ int remote_has_url(struct remote *remote, const char *url) static int match_name_with_pattern(const char *key, const char *name, const char *value, char **result) { - size_t klen = strlen(key); - int ret = !strncmp(key, name, klen); + const char *kstar = strchr(key, '*'); + size_t klen; + int ret; + if (!kstar) + die("Key '%s' of pattern had no '*'", key); + klen = kstar - key; + ret = !strncmp(key, name, klen); if (ret && value) { - size_t vlen = strlen(value); + const char *vstar = strchr(value, '*'); + size_t vlen; + if (!vstar) + die("Value '%s' of pattern has no '*'", value); + vlen = vstar - value; *result = xmalloc(vlen + strlen(name) - klen + 1); From abd2bde78bd994166900290434a2048e660dabed Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Sat, 7 Mar 2009 01:11:39 -0500 Subject: [PATCH 061/289] Support '*' in the middle of a refspec In order to keep the requirements strict, each * has to be a full path component, and there may only be one * per side. This requirement is enforced entirely by check_ref_format(); the matching implementation will substitute the whatever matches the * in the lhs for the * in the rhs. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- refs.c | 11 +++++++---- remote.c | 24 +++++++++++++++++------- t/t5511-refspec.sh | 12 ++++++++++++ 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/refs.c b/refs.c index a50ba79270..fef7c9f26d 100644 --- a/refs.c +++ b/refs.c @@ -694,6 +694,7 @@ static inline int bad_ref_char(int ch) int check_ref_format(const char *ref) { int ch, level, bad_type; + int ret = CHECK_REF_FORMAT_OK; const char *cp = ref; level = 0; @@ -709,9 +710,11 @@ int check_ref_format(const char *ref) return CHECK_REF_FORMAT_ERROR; bad_type = bad_ref_char(ch); if (bad_type) { - return (bad_type == 2 && !*cp) - ? CHECK_REF_FORMAT_WILDCARD - : CHECK_REF_FORMAT_ERROR; + if (bad_type == 2 && (!*cp || *cp == '/') && + ret == CHECK_REF_FORMAT_OK) + ret = CHECK_REF_FORMAT_WILDCARD; + else + return CHECK_REF_FORMAT_ERROR; } /* scan the rest of the path component */ @@ -729,7 +732,7 @@ int check_ref_format(const char *ref) if (!ch) { if (level < 2) return CHECK_REF_FORMAT_ONELEVEL; - return CHECK_REF_FORMAT_OK; + return ret; } } } diff --git a/remote.c b/remote.c index d596a48651..90203e2fe1 100644 --- a/remote.c +++ b/remote.c @@ -511,12 +511,12 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp if (rhs) { size_t rlen = strlen(++rhs); - is_glob = (2 <= rlen && !strcmp(rhs + rlen - 2, "/*")); + is_glob = (1 <= rlen && strchr(rhs, '*')); rs[i].dst = xstrndup(rhs, rlen); } llen = (rhs ? (rhs - lhs - 1) : strlen(lhs)); - if (2 <= llen && !memcmp(lhs + llen - 2, "/*", 2)) { + if (1 <= llen && memchr(lhs, '*', llen)) { if ((rhs && !is_glob) || (!rhs && fetch)) goto invalid; is_glob = 1; @@ -718,22 +718,32 @@ static int match_name_with_pattern(const char *key, const char *name, { const char *kstar = strchr(key, '*'); size_t klen; + size_t ksuffixlen; + size_t namelen; int ret; if (!kstar) die("Key '%s' of pattern had no '*'", key); klen = kstar - key; - ret = !strncmp(key, name, klen); + ksuffixlen = strlen(kstar + 1); + namelen = strlen(name); + ret = !strncmp(name, key, klen) && namelen >= klen + ksuffixlen && + !memcmp(name + namelen - ksuffixlen, kstar + 1, ksuffixlen); if (ret && value) { const char *vstar = strchr(value, '*'); size_t vlen; + size_t vsuffixlen; if (!vstar) die("Value '%s' of pattern has no '*'", value); vlen = vstar - value; - *result = xmalloc(vlen + + vsuffixlen = strlen(vstar + 1); + *result = xmalloc(vlen + vsuffixlen + strlen(name) - - klen + 1); - strcpy(*result, value); - strcpy(*result + vlen, name + klen); + klen - ksuffixlen + 1); + strncpy(*result, value, vlen); + strncpy(*result + vlen, + name + klen, namelen - klen - ksuffixlen); + strcpy(*result + vlen + namelen - klen - ksuffixlen, + vstar + 1); } return ret; } diff --git a/t/t5511-refspec.sh b/t/t5511-refspec.sh index 22ba380034..c28932216b 100755 --- a/t/t5511-refspec.sh +++ b/t/t5511-refspec.sh @@ -72,4 +72,16 @@ test_refspec fetch ':refs/remotes/frotz/HEAD-to-me' test_refspec push ':refs/remotes/frotz/delete me' invalid test_refspec fetch ':refs/remotes/frotz/HEAD to me' invalid +test_refspec fetch 'refs/heads/*/for-linus:refs/remotes/mine/*-blah' invalid +test_refspec push 'refs/heads/*/for-linus:refs/remotes/mine/*-blah' invalid + +test_refspec fetch 'refs/heads*/for-linus:refs/remotes/mine/*' invalid +test_refspec push 'refs/heads*/for-linus:refs/remotes/mine/*' invalid + +test_refspec fetch 'refs/heads/*/*/for-linus:refs/remotes/mine/*' invalid +test_refspec push 'refs/heads/*/*/for-linus:refs/remotes/mine/*' invalid + +test_refspec fetch 'refs/heads/*/for-linus:refs/remotes/mine/*' +test_refspec push 'refs/heads/*/for-linus:refs/remotes/mine/*' + test_done From 8a3b25da8cdd7f3aa75f18d3245ce83aca90bb1a Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sat, 7 Mar 2009 20:22:07 -0500 Subject: [PATCH 062/289] t3000: use test_cmp instead of diff These ancient tests predate test_cmp. While we're at it, let's switch to our usual "expected before actual" order of arguments; this makes the diff output "here's what is changed from expected" instead of the reverse. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- t/t3000-ls-files-others.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/t/t3000-ls-files-others.sh b/t/t3000-ls-files-others.sh index bc0a351392..36eee0f8ae 100755 --- a/t/t3000-ls-files-others.sh +++ b/t/t3000-ls-files-others.sh @@ -42,7 +42,7 @@ test_expect_success \ test_expect_success \ 'git ls-files --others should pick up symlinks.' \ - 'diff output expected1' + 'test_cmp expected1 output' test_expect_success \ 'git ls-files --others --directory to show output.' \ @@ -51,6 +51,6 @@ test_expect_success \ test_expect_success \ 'git ls-files --others --directory should not get confused.' \ - 'diff output expected2' + 'test_cmp expected2 output' test_done From 2fb6d6d6dd1033bfe82d6d327ac270f9cf8943cd Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sat, 7 Mar 2009 20:27:22 -0500 Subject: [PATCH 063/289] ls-files: fix broken --no-empty-directory Commit ce8e880 converted ls-files to use parseopt; the --no-empty-directory option was converted as an OPT_BIT for "empty-directory" to set the DIR_HIDE_EMPTY_DIRECTORY flag. However, that makes it do the opposite of what it should: --empty-directory would hide, but --no-empty-directory would turn off hiding. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin-ls-files.c | 4 ++-- t/t3000-ls-files-others.sh | 14 +++++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/builtin-ls-files.c b/builtin-ls-files.c index 1742c0f80d..437c366c9e 100644 --- a/builtin-ls-files.c +++ b/builtin-ls-files.c @@ -454,8 +454,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix) OPT_BIT(0, "directory", &dir.flags, "show 'other' directories' name only", DIR_SHOW_OTHER_DIRECTORIES), - OPT_BIT(0, "empty-directory", &dir.flags, - "list empty directories", + OPT_BIT(0, "no-empty-directory", &dir.flags, + "don't show empty directories", DIR_HIDE_EMPTY_DIRECTORIES), OPT_BOOLEAN('u', "unmerged", &show_unmerged, "show unmerged files in the output"), diff --git a/t/t3000-ls-files-others.sh b/t/t3000-ls-files-others.sh index 36eee0f8ae..379d963cea 100755 --- a/t/t3000-ls-files-others.sh +++ b/t/t3000-ls-files-others.sh @@ -13,12 +13,13 @@ filesystem. path2/file2 - a file in a directory path3-junk - a file to confuse things path3/file3 - a file in a directory + path4 - an empty directory ' . ./test-lib.sh date >path0 ln -s xyzzy path1 -mkdir path2 path3 +mkdir path2 path3 path4 date >path2/file2 date >path2-junk date >path3/file3 @@ -28,6 +29,7 @@ git update-index --add path3-junk path3/file3 cat >expected1 <expected2 +cat expected3 +echo path4/ >>expected2 test_expect_success \ 'git ls-files --others to show output.' \ @@ -53,4 +57,12 @@ test_expect_success \ 'git ls-files --others --directory should not get confused.' \ 'test_cmp expected2 output' +test_expect_success \ + 'git ls-files --others --directory --no-empty-directory to show output.' \ + 'git ls-files --others --directory --no-empty-directory >output' + +test_expect_success \ + '--no-empty-directory hides empty directory' \ + 'test_cmp expected3 output' + test_done From c06ff4908bf9ad8bf2448439a3574321c9399b17 Mon Sep 17 00:00:00 2001 From: Kjetil Barvik Date: Wed, 4 Mar 2009 18:47:40 +0100 Subject: [PATCH 064/289] Record ns-timestamps if possible, but do not use it without USE_NSEC Traditionally, the lack of USE_NSEC meant "do not record nor use the nanosecond resolution part of the file timestamps". To avoid problems on filesystems that lose the ns part when the metadata is flushed to the disk and then later read back in, disabling USE_NSEC has been a good idea in general. If you are on a filesystem without such an issue, it does not hurt to read and store them in the cached stat data in the index entries even if your git is compiled without USE_NSEC. The index left with such a version of git can be read by git compiled with USE_NSEC and it can make use of the nanosecond part to optimize the check to see if the path on the filesystem hsa been modified since we last looked at. Signed-off-by: Junio C Hamano --- Makefile | 8 ++++++++ builtin-fetch-pack.c | 4 +--- git-compat-util.h | 9 +++++++++ read-cache.c | 29 ++++------------------------- unpack-trees.c | 2 -- 5 files changed, 22 insertions(+), 30 deletions(-) diff --git a/Makefile b/Makefile index 27b9569746..65b5b8a63f 100644 --- a/Makefile +++ b/Makefile @@ -126,6 +126,9 @@ all:: # randomly break unless your underlying filesystem supports those sub-second # times (my ext3 doesn't). # +# Define NO_NSEC if your "struct stat" does not have "st_ctim.tv_nsec" +# available. This automatically turns USE_NSEC off. +# # Define USE_STDEV below if you want git to care about the underlying device # change being considered an inode change from the update-index perspective. # @@ -737,6 +740,7 @@ ifeq ($(uname_S),AIX) NO_MEMMEM = YesPlease NO_MKDTEMP = YesPlease NO_STRLCPY = YesPlease + NO_NSEC = YesPlease FREAD_READS_DIRECTORIES = UnfortunatelyYes INTERNAL_QSORT = UnfortunatelyYes NEEDS_LIBICONV=YesPlease @@ -802,6 +806,7 @@ ifneq (,$(findstring MINGW,$(uname_S))) RUNTIME_PREFIX = YesPlease NO_POSIX_ONLY_PROGRAMS = YesPlease NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease + NO_NSEC = YesPlease COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/regex -Icompat/fnmatch COMPAT_CFLAGS += -DSNPRINTF_SIZE_CORR=1 COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\" @@ -923,6 +928,9 @@ endif ifdef NO_ST_BLOCKS_IN_STRUCT_STAT BASIC_CFLAGS += -DNO_ST_BLOCKS_IN_STRUCT_STAT endif +ifdef NO_NSEC + BASIC_CFLAGS += -DNO_NSEC +endif ifdef NO_C99_FORMAT BASIC_CFLAGS += -DNO_C99_FORMAT endif diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c index 3b210c7fdf..59b0b0a796 100644 --- a/builtin-fetch-pack.c +++ b/builtin-fetch-pack.c @@ -801,9 +801,7 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args, int fd; mtime.sec = st.st_mtime; -#ifdef USE_NSEC - mtime.nsec = st.st_mtim.tv_nsec; -#endif + mtime.nsec = ST_MTIME_NSEC(st); if (stat(shallow, &st)) { if (mtime.sec) die("shallow file was removed during fetch"); diff --git a/git-compat-util.h b/git-compat-util.h index 079cbe9440..9b495dcad8 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -384,4 +384,13 @@ void git_qsort(void *base, size_t nmemb, size_t size, # define FORCE_DIR_SET_GID 0 #endif +#ifdef NO_NSEC +#undef USE_NSEC +#define ST_CTIME_NSEC(st) 0 +#define ST_MTIME_NSEC(st) 0 +#else +#define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctim.tv_nsec)) +#define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtim.tv_nsec)) +#endif + #endif diff --git a/read-cache.c b/read-cache.c index 91f1d03c09..b819abbd00 100644 --- a/read-cache.c +++ b/read-cache.c @@ -69,13 +69,8 @@ void fill_stat_cache_info(struct cache_entry *ce, struct stat *st) { ce->ce_ctime.sec = (unsigned int)st->st_ctime; ce->ce_mtime.sec = (unsigned int)st->st_mtime; -#ifdef USE_NSEC - ce->ce_ctime.nsec = (unsigned int)st->st_ctim.tv_nsec; - ce->ce_mtime.nsec = (unsigned int)st->st_mtim.tv_nsec; -#else - ce->ce_ctime.nsec = 0; - ce->ce_mtime.nsec = 0; -#endif + ce->ce_ctime.nsec = ST_CTIME_NSEC(*st); + ce->ce_mtime.nsec = ST_MTIME_NSEC(*st); ce->ce_dev = st->st_dev; ce->ce_ino = st->st_ino; ce->ce_uid = st->st_uid; @@ -1183,13 +1178,8 @@ static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_en ce->ce_ctime.sec = ntohl(ondisk->ctime.sec); ce->ce_mtime.sec = ntohl(ondisk->mtime.sec); -#ifdef USE_NSEC ce->ce_ctime.nsec = ntohl(ondisk->ctime.nsec); ce->ce_mtime.nsec = ntohl(ondisk->mtime.nsec); -#else - ce->ce_ctime.nsec = 0; - ce->ce_mtime.nsec = 0; -#endif ce->ce_dev = ntohl(ondisk->dev); ce->ce_ino = ntohl(ondisk->ino); ce->ce_mode = ntohl(ondisk->mode); @@ -1309,11 +1299,7 @@ int read_index_from(struct index_state *istate, const char *path) dst_offset += ce_size(ce); } istate->timestamp.sec = st.st_mtime; -#ifdef USE_NSEC - istate->timestamp.nsec = (unsigned int)st.st_mtim.tv_nsec; -#else - istate->timestamp.nsec = 0; -#endif + istate->timestamp.nsec = ST_MTIME_NSEC(st); while (src_offset <= mmap_size - 20 - 8) { /* After an array of active_nr index entries, @@ -1500,13 +1486,8 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce) ondisk->ctime.sec = htonl(ce->ce_ctime.sec); ondisk->mtime.sec = htonl(ce->ce_mtime.sec); -#ifdef USE_NSEC ondisk->ctime.nsec = htonl(ce->ce_ctime.nsec); ondisk->mtime.nsec = htonl(ce->ce_mtime.nsec); -#else - ondisk->ctime.nsec = 0; - ondisk->mtime.nsec = 0; -#endif ondisk->dev = htonl(ce->ce_dev); ondisk->ino = htonl(ce->ce_ino); ondisk->mode = htonl(ce->ce_mode); @@ -1583,9 +1564,7 @@ int write_index(struct index_state *istate, int newfd) if (ce_flush(&c, newfd) || fstat(newfd, &st)) return -1; istate->timestamp.sec = (unsigned int)st.st_ctime; -#ifdef USE_NSEC - istate->timestamp.nsec = (unsigned int)st.st_ctim.tv_nsec; -#endif + istate->timestamp.nsec = ST_CTIME_NSEC(st); return 0; } diff --git a/unpack-trees.c b/unpack-trees.c index 9fe0cd5f9b..da2e3c0915 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -362,9 +362,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options o->result.initialized = 1; if (o->src_index) { o->result.timestamp.sec = o->src_index->timestamp.sec; -#ifdef USE_NSEC o->result.timestamp.nsec = o->src_index->timestamp.nsec; -#endif } o->merge_size = len; From 8321c56b6bae25a2d70790f452df894be536b32c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 8 Mar 2009 00:10:33 -0800 Subject: [PATCH 065/289] builtin-remote.c: no "commented out" code, please And especially do not use // comment. Signed-off-by: Junio C Hamano --- builtin-remote.c | 1 - 1 file changed, 1 deletion(-) diff --git a/builtin-remote.c b/builtin-remote.c index 7e82a52b7d..7b31e554e9 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -333,7 +333,6 @@ static int get_push_ref_states(const struct ref *remote_refs, info->status = PUSH_STATUS_FASTFORWARD; else info->status = PUSH_STATUS_OUTOFDATE; - // ref->peer_ref = NULL; /* local ref which is freed below */ } free_refs(local_refs); free_refs(push_map); From 110c46a909fe27f5b8aff412a78cb821300fb985 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 8 Mar 2009 13:51:33 -0700 Subject: [PATCH 066/289] Not all systems use st_[cm]tim field for ns resolution file timestamp Some codepaths do not still use the ST_[CM]TIME_NSEC() pair of macros introduced by the previous commit but assumes all systems use st_mtim and st_ctim fields in "struct stat" to record nanosecond resolution part of the file timestamps. Signed-off-by: Junio C Hamano --- builtin-fetch-pack.c | 2 +- read-cache.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c index 59b0b0a796..1d7e02326f 100644 --- a/builtin-fetch-pack.c +++ b/builtin-fetch-pack.c @@ -807,7 +807,7 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args, die("shallow file was removed during fetch"); } else if (st.st_mtime != mtime.sec #ifdef USE_NSEC - || st.st_mtim.tv_nsec != mtime.nsec + || ST_CTIME_NSEC(st) != mtime.nsec #endif ) die("shallow file was changed during fetch"); diff --git a/read-cache.c b/read-cache.c index b819abbd00..7f74c8d161 100644 --- a/read-cache.c +++ b/read-cache.c @@ -204,9 +204,9 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st) changed |= CTIME_CHANGED; #ifdef USE_NSEC - if (ce->ce_mtime.nsec != (unsigned int)st->st_mtim.tv_nsec) + if (ce->ce_mtime.nsec != ST_MTIME_NSEC(*st)) changed |= MTIME_CHANGED; - if (trust_ctime && ce->ce_ctime.nsec != (unsigned int)st->st_ctim.tv_nsec) + if (trust_ctime && ce->ce_ctime.nsec != ST_CTIME_NSEC(*st)) changed |= CTIME_CHANGED; #endif From c567383b1e3205c895b371d42a39fbdf131032ba Mon Sep 17 00:00:00 2001 From: Brian Gernhardt Date: Sun, 8 Mar 2009 16:04:28 -0400 Subject: [PATCH 067/289] Create USE_ST_TIMESPEC and turn it on for Darwin Not all OSes use st_ctim and st_mtim in their struct stat. In particular, it appears that OS X uses st_*timespec instead. So add a Makefile variable and #define called USE_ST_TIMESPEC to switch the USE_NSEC defines to use st_*timespec. This also turns it on by default for OS X (Darwin) machines. Likely this is a sane default for other BSD kernels as well, but I don't have any to test that assumption on. Signed-off-by: Brian Gernhardt Signed-off-by: Junio C Hamano --- Makefile | 7 +++++++ git-compat-util.h | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/Makefile b/Makefile index 65b5b8a63f..7b310859e9 100644 --- a/Makefile +++ b/Makefile @@ -126,6 +126,9 @@ all:: # randomly break unless your underlying filesystem supports those sub-second # times (my ext3 doesn't). # +# Define USE_ST_TIMESPEC if your "struct stat" uses "st_ctimespec" instead of +# "st_ctim" +# # Define NO_NSEC if your "struct stat" does not have "st_ctim.tv_nsec" # available. This automatically turns USE_NSEC off. # @@ -663,6 +666,7 @@ ifeq ($(uname_S),Darwin) endif NO_MEMMEM = YesPlease THREADED_DELTA_SEARCH = YesPlease + USE_ST_TIMESPEC = YesPlease endif ifeq ($(uname_S),SunOS) NEEDS_SOCKET = YesPlease @@ -928,6 +932,9 @@ endif ifdef NO_ST_BLOCKS_IN_STRUCT_STAT BASIC_CFLAGS += -DNO_ST_BLOCKS_IN_STRUCT_STAT endif +ifdef USE_ST_TIMESPEC + BASIC_CFLAGS += -DUSE_ST_TIMESPEC +endif ifdef NO_NSEC BASIC_CFLAGS += -DNO_NSEC endif diff --git a/git-compat-util.h b/git-compat-util.h index 9b495dcad8..c915752a24 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -389,8 +389,13 @@ void git_qsort(void *base, size_t nmemb, size_t size, #define ST_CTIME_NSEC(st) 0 #define ST_MTIME_NSEC(st) 0 #else +#ifdef USE_ST_TIMESPEC +#define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctimespec.tv_nsec)) +#define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtimespec.tv_nsec)) +#else #define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctim.tv_nsec)) #define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtim.tv_nsec)) #endif +#endif #endif From d7371a2d4d308d961d22c12ae9bc3d28cf35f029 Mon Sep 17 00:00:00 2001 From: Brian Gernhardt Date: Sun, 8 Mar 2009 17:22:51 -0400 Subject: [PATCH 068/289] Makefile: Set compiler switch for USE_NSEC The comments indicated that setting a Makefile variable USE_NSEC would enable the code for sub-second [cm]times. However, the Makefile variable was never turned into a compiler switch so the code was never enabled. This patch allows USE_NSEC to be noticed by the compiler. Signed-off-by: Brian Gernhardt Signed-off-by: Junio C Hamano --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 7b310859e9..e8cdbfb3d0 100644 --- a/Makefile +++ b/Makefile @@ -932,6 +932,9 @@ endif ifdef NO_ST_BLOCKS_IN_STRUCT_STAT BASIC_CFLAGS += -DNO_ST_BLOCKS_IN_STRUCT_STAT endif +ifdef USE_NSEC + BASIC_CFLAGS += -DUSE_NSEC +endif ifdef USE_ST_TIMESPEC BASIC_CFLAGS += -DUSE_ST_TIMESPEC endif From 747a322bcc4df5f9a371890ffe728741456704c7 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 8 Mar 2009 18:22:44 -0700 Subject: [PATCH 069/289] grep: cast printf %.*s "precision" argument explicitly to int On some systems, regoff_t that is the type of rm_so/rm_eo members are wider than int; %.*s precision specifier expects an int, so use an explicit cast. A breakage reported on Darwin by Brian Gernhardt should be fixed with this patch. Signed-off-by: Junio C Hamano --- grep.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grep.c b/grep.c index cace1c8bcb..be99b34168 100644 --- a/grep.c +++ b/grep.c @@ -490,9 +490,9 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol, *eol = '\0'; while (next_match(opt, bol, eol, ctx, &match, eflags)) { printf("%.*s%s%.*s%s", - match.rm_so, bol, + (int)match.rm_so, bol, opt->color_match, - match.rm_eo - match.rm_so, bol + match.rm_so, + (int)(match.rm_eo - match.rm_so), bol + match.rm_so, GIT_COLOR_RESET); bol += match.rm_eo; rest -= match.rm_eo; From a9c37a72c4fbc8537de294d66b05bdfd7f9a4016 Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Sun, 8 Mar 2009 21:06:05 -0400 Subject: [PATCH 070/289] Use a common function to get the pretty name of refs The result should be consistent between fetch and push, so we ought to use the same code in both cases, even though it's short. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- builtin-fetch.c | 6 +----- builtin-send-pack.c | 10 ---------- refs.c | 10 ++++++++++ refs.h | 2 ++ 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/builtin-fetch.c b/builtin-fetch.c index 1e4a3d9c51..f3bdeda059 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -197,11 +197,7 @@ static int update_local_ref(struct ref *ref, struct commit *current = NULL, *updated; enum object_type type; struct branch *current_branch = branch_get(NULL); - const char *pretty_ref = ref->name + ( - !prefixcmp(ref->name, "refs/heads/") ? 11 : - !prefixcmp(ref->name, "refs/tags/") ? 10 : - !prefixcmp(ref->name, "refs/remotes/") ? 13 : - 0); + const char *pretty_ref = prettify_ref(ref); *display = 0; type = sha1_object_info(ref->new_sha1, NULL); diff --git a/builtin-send-pack.c b/builtin-send-pack.c index 9072905f10..43b89ece36 100644 --- a/builtin-send-pack.c +++ b/builtin-send-pack.c @@ -172,16 +172,6 @@ static void update_tracking_ref(struct remote *remote, struct ref *ref) } } -static const char *prettify_ref(const struct ref *ref) -{ - const char *name = ref->name; - return name + ( - !prefixcmp(name, "refs/heads/") ? 11 : - !prefixcmp(name, "refs/tags/") ? 10 : - !prefixcmp(name, "refs/remotes/") ? 13 : - 0); -} - #define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3) static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg) diff --git a/refs.c b/refs.c index b2a37e1185..8f968b51a1 100644 --- a/refs.c +++ b/refs.c @@ -736,6 +736,16 @@ int check_ref_format(const char *ref) } } +const char *prettify_ref(const struct ref *ref) +{ + const char *name = ref->name; + return name + ( + !prefixcmp(name, "refs/heads/") ? 11 : + !prefixcmp(name, "refs/tags/") ? 10 : + !prefixcmp(name, "refs/remotes/") ? 13 : + 0); +} + const char *ref_rev_parse_rules[] = { "%.*s", "refs/%.*s", diff --git a/refs.h b/refs.h index 29bdcecd4e..68c2d16d53 100644 --- a/refs.h +++ b/refs.h @@ -79,6 +79,8 @@ extern int for_each_reflog(each_ref_fn, void *); #define CHECK_REF_FORMAT_WILDCARD (-3) extern int check_ref_format(const char *target); +extern const char *prettify_ref(const struct ref *ref); + /** rename ref, return 0 on success **/ extern int rename_ref(const char *oldref, const char *newref, const char *logmsg); From 64fcef2daa03f6093b480142c6ab2a4173b0b43e Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Sun, 8 Mar 2009 21:06:07 -0400 Subject: [PATCH 071/289] Move push matching and reporting logic into transport.c For native-protocol pushes (and other protocols as they are converted to the new method), this moves the refspec match, tracking update, and report message out of send-pack() and into transport_push(), where it can be shared completely with other protocols. This also makes fetch and push more similar in terms of what code is in what file. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- builtin-send-pack.c | 143 +++++++++++----------- send-pack.h | 6 +- transport.c | 283 +++++++++++++++++++++++++++++++++++++++++--- transport.h | 3 +- 4 files changed, 343 insertions(+), 92 deletions(-) diff --git a/builtin-send-pack.c b/builtin-send-pack.c index 43b89ece36..91c36512a8 100644 --- a/builtin-send-pack.c +++ b/builtin-send-pack.c @@ -11,7 +11,6 @@ static const char send_pack_usage[] = " --all and explicit specification are mutually exclusive."; static struct send_pack_args args = { - /* .receivepack = */ "git-receive-pack", }; static int feed_object(const unsigned char *sha1, int fd, int negative) @@ -31,7 +30,7 @@ static int feed_object(const unsigned char *sha1, int fd, int negative) /* * Make a pack stream and spit it out into file descriptor fd */ -static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *extra) +static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *extra, struct send_pack_args *args) { /* * The child becomes pack-objects --revs; we feed @@ -49,7 +48,7 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext struct child_process po; int i; - if (args.use_thin_pack) + if (args->use_thin_pack) argv[4] = "--thin"; memset(&po, 0, sizeof(po)); po.argv = argv; @@ -83,8 +82,6 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext return 0; } -static struct ref *remote_refs, **remote_tail; - static int receive_status(int in, struct ref *refs) { struct ref *hint; @@ -300,27 +297,19 @@ static int refs_pushed(struct ref *ref) return 0; } -static int do_send_pack(int in, int out, struct remote *remote, const char *dest, int nr_refspec, const char **refspec) +int send_pack(struct send_pack_args *args, + int fd[], struct child_process *conn, + struct ref *remote_refs, + struct extra_have_objects *extra_have) { - struct ref *ref, *local_refs; + int in = fd[0]; + int out = fd[1]; + struct ref *ref; int new_refs; int ask_for_status_report = 0; int allow_deleting_refs = 0; int expect_status_report = 0; - int flags = MATCH_REFS_NONE; int ret; - struct extra_have_objects extra_have; - - memset(&extra_have, 0, sizeof(extra_have)); - if (args.send_all) - flags |= MATCH_REFS_ALL; - if (args.send_mirror) - flags |= MATCH_REFS_MIRROR; - - /* No funny business with the matcher */ - remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL, - &extra_have); - local_refs = get_local_heads(); /* Does the other end support the reporting? */ if (server_supports("report-status")) @@ -328,19 +317,9 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest if (server_supports("delete-refs")) allow_deleting_refs = 1; - /* match them up */ - if (!remote_tail) - remote_tail = &remote_refs; - if (match_refs(local_refs, remote_refs, &remote_tail, - nr_refspec, refspec, flags)) { - close(out); - return -1; - } - if (!remote_refs) { fprintf(stderr, "No refs in common and none specified; doing nothing.\n" "Perhaps you should specify a branch such as 'master'.\n"); - close(out); return 0; } @@ -352,7 +331,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest if (ref->peer_ref) hashcpy(ref->new_sha1, ref->peer_ref->new_sha1); - else if (!args.send_mirror) + else if (!args->send_mirror) continue; ref->deletion = is_null_sha1(ref->new_sha1); @@ -391,7 +370,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest (!has_sha1_file(ref->old_sha1) || !ref_newer(ref->new_sha1, ref->old_sha1)); - if (ref->nonfastforward && !ref->force && !args.force_update) { + if (ref->nonfastforward && !ref->force && !args->force_update) { ref->status = REF_STATUS_REJECT_NONFASTFORWARD; continue; } @@ -399,7 +378,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest if (!ref->deletion) new_refs++; - if (!args.dry_run) { + if (!args->dry_run) { char *old_hex = sha1_to_hex(ref->old_sha1); char *new_hex = sha1_to_hex(ref->new_sha1); @@ -420,27 +399,19 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest } packet_flush(out); - if (new_refs && !args.dry_run) { - if (pack_objects(out, remote_refs, &extra_have) < 0) + if (new_refs && !args->dry_run) { + if (pack_objects(out, remote_refs, extra_have, args) < 0) { + for (ref = remote_refs; ref; ref = ref->next) + ref->status = REF_STATUS_NONE; return -1; + } } - else - close(out); if (expect_status_report) ret = receive_status(in, remote_refs); else ret = 0; - print_push_status(dest, remote_refs); - - if (!args.dry_run && remote) { - for (ref = remote_refs; ref; ref = ref->next) - update_tracking_ref(remote, ref); - } - - if (!refs_pushed(remote_refs)) - fprintf(stderr, "Everything up-to-date\n"); if (ret < 0) return ret; for (ref = remote_refs; ref; ref = ref->next) { @@ -489,11 +460,19 @@ static void verify_remote_names(int nr_heads, const char **heads) int cmd_send_pack(int argc, const char **argv, const char *prefix) { - int i, nr_heads = 0; - const char **heads = NULL; + int i, nr_refspecs = 0; + const char **refspecs = NULL; const char *remote_name = NULL; struct remote *remote = NULL; const char *dest = NULL; + int fd[2]; + struct child_process *conn; + struct extra_have_objects extra_have; + struct ref *remote_refs, **remote_tail, *local_refs; + int ret; + int send_all = 0; + const char *receivepack = "git-receive-pack"; + int flags; argv++; for (i = 1; i < argc; i++, argv++) { @@ -501,11 +480,11 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) if (*arg == '-') { if (!prefixcmp(arg, "--receive-pack=")) { - args.receivepack = arg + 15; + receivepack = arg + 15; continue; } if (!prefixcmp(arg, "--exec=")) { - args.receivepack = arg + 7; + receivepack = arg + 7; continue; } if (!prefixcmp(arg, "--remote=")) { @@ -513,7 +492,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) continue; } if (!strcmp(arg, "--all")) { - args.send_all = 1; + send_all = 1; continue; } if (!strcmp(arg, "--dry-run")) { @@ -542,8 +521,8 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) dest = arg; continue; } - heads = (const char **) argv; - nr_heads = argc - i; + refspecs = (const char **) argv; + nr_refspecs = argc - i; break; } if (!dest) @@ -552,8 +531,8 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) * --all and --mirror are incompatible; neither makes sense * with any refspecs. */ - if ((heads && (args.send_all || args.send_mirror)) || - (args.send_all && args.send_mirror)) + if ((refspecs && (send_all || args.send_mirror)) || + (send_all && args.send_mirror)) usage(send_pack_usage); if (remote_name) { @@ -564,24 +543,50 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) } } - return send_pack(&args, dest, remote, nr_heads, heads); -} + conn = git_connect(fd, dest, receivepack, args.verbose ? CONNECT_VERBOSE : 0); -int send_pack(struct send_pack_args *my_args, - const char *dest, struct remote *remote, - int nr_heads, const char **heads) -{ - int fd[2], ret; - struct child_process *conn; + memset(&extra_have, 0, sizeof(extra_have)); - memcpy(&args, my_args, sizeof(args)); + get_remote_heads(fd[0], &remote_refs, 0, NULL, REF_NORMAL, + &extra_have); - verify_remote_names(nr_heads, heads); + verify_remote_names(nr_refspecs, refspecs); - conn = git_connect(fd, dest, args.receivepack, args.verbose ? CONNECT_VERBOSE : 0); - ret = do_send_pack(fd[0], fd[1], remote, dest, nr_heads, heads); + local_refs = get_local_heads(); + + flags = MATCH_REFS_NONE; + + if (send_all) + flags |= MATCH_REFS_ALL; + if (args.send_mirror) + flags |= MATCH_REFS_MIRROR; + + /* match them up */ + remote_tail = &remote_refs; + while (*remote_tail) + remote_tail = &((*remote_tail)->next); + if (match_refs(local_refs, remote_refs, &remote_tail, + nr_refspecs, refspecs, flags)) { + return -1; + } + + ret = send_pack(&args, fd, conn, remote_refs, &extra_have); + + close(fd[1]); close(fd[0]); - /* do_send_pack always closes fd[1] */ + ret |= finish_connect(conn); - return !!ret; + + print_push_status(dest, remote_refs); + + if (!args.dry_run && remote) { + struct ref *ref; + for (ref = remote_refs; ref; ref = ref->next) + update_tracking_ref(remote, ref); + } + + if (!ret && !refs_pushed(remote_refs)) + fprintf(stderr, "Everything up-to-date\n"); + + return ret; } diff --git a/send-pack.h b/send-pack.h index 8ff1dc3539..83d76c7e35 100644 --- a/send-pack.h +++ b/send-pack.h @@ -2,9 +2,7 @@ #define SEND_PACK_H struct send_pack_args { - const char *receivepack; unsigned verbose:1, - send_all:1, send_mirror:1, force_update:1, use_thin_pack:1, @@ -12,7 +10,7 @@ struct send_pack_args { }; int send_pack(struct send_pack_args *args, - const char *dest, struct remote *remote, - int nr_heads, const char **heads); + int fd[], struct child_process *conn, + struct ref *remote_refs, struct extra_have_objects *extra_have); #endif diff --git a/transport.c b/transport.c index 9ad4a16c31..73bb9b50be 100644 --- a/transport.c +++ b/transport.c @@ -138,7 +138,7 @@ static void insert_packed_refs(const char *packed_refs, struct ref **list) } } -static struct ref *get_refs_via_rsync(struct transport *transport) +static struct ref *get_refs_via_rsync(struct transport *transport, int for_push) { struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT; struct ref dummy, *tail = &dummy; @@ -146,6 +146,9 @@ static struct ref *get_refs_via_rsync(struct transport *transport) const char *args[5]; int temp_dir_len; + if (for_push) + return NULL; + /* copy the refs to the temporary directory */ strbuf_addstr(&temp_dir, git_path("rsync-refs-XXXXXX")); @@ -422,7 +425,7 @@ static int curl_transport_push(struct transport *transport, int refspec_nr, cons return !!err; } -static struct ref *get_refs_via_curl(struct transport *transport) +static struct ref *get_refs_via_curl(struct transport *transport, int for_push) { struct strbuf buffer = STRBUF_INIT; char *data, *start, *mid; @@ -439,6 +442,9 @@ static struct ref *get_refs_via_curl(struct transport *transport) struct walker *walker; + if (for_push) + return NULL; + if (!transport->data) transport->data = get_http_walker(transport->url, transport->remote); @@ -525,12 +531,15 @@ struct bundle_transport_data { struct bundle_header header; }; -static struct ref *get_refs_from_bundle(struct transport *transport) +static struct ref *get_refs_from_bundle(struct transport *transport, int for_push) { struct bundle_transport_data *data = transport->data; struct ref *result = NULL; int i; + if (for_push) + return NULL; + if (data->fd > 0) close(data->fd); data->fd = read_bundle_header(transport->url, &data->header); @@ -571,6 +580,7 @@ struct git_transport_data { int fd[2]; const char *uploadpack; const char *receivepack; + struct extra_have_objects extra_have; }; static int set_git_option(struct transport *connection, @@ -602,20 +612,23 @@ static int set_git_option(struct transport *connection, return 1; } -static int connect_setup(struct transport *transport) +static int connect_setup(struct transport *transport, int for_push, int verbose) { struct git_transport_data *data = transport->data; - data->conn = git_connect(data->fd, transport->url, data->uploadpack, 0); + data->conn = git_connect(data->fd, transport->url, + for_push ? data->receivepack : data->uploadpack, + verbose ? CONNECT_VERBOSE : 0); return 0; } -static struct ref *get_refs_via_connect(struct transport *transport) +static struct ref *get_refs_via_connect(struct transport *transport, int for_push) { struct git_transport_data *data = transport->data; struct ref *refs; - connect_setup(transport); - get_remote_heads(data->fd[0], &refs, 0, NULL, 0, NULL); + connect_setup(transport, for_push, 0); + get_remote_heads(data->fd[0], &refs, 0, NULL, + for_push ? REF_NORMAL : 0, &data->extra_have); return refs; } @@ -647,7 +660,7 @@ static int fetch_refs_via_pack(struct transport *transport, origh[i] = heads[i] = xstrdup(to_fetch[i]->name); if (!data->conn) { - connect_setup(transport); + connect_setup(transport, 0, 0); get_remote_heads(data->fd[0], &refs_tmp, 0, NULL, 0, NULL); } @@ -670,20 +683,216 @@ static int fetch_refs_via_pack(struct transport *transport, return (refs ? 0 : -1); } -static int git_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) +static int refs_pushed(struct ref *ref) +{ + for (; ref; ref = ref->next) { + switch(ref->status) { + case REF_STATUS_NONE: + case REF_STATUS_UPTODATE: + break; + default: + return 1; + } + } + return 0; +} + +static void update_tracking_ref(struct remote *remote, struct ref *ref, int verbose) +{ + struct refspec rs; + + if (ref->status != REF_STATUS_OK && ref->status != REF_STATUS_UPTODATE) + return; + + rs.src = ref->name; + rs.dst = NULL; + + if (!remote_find_tracking(remote, &rs)) { + if (verbose) + fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst); + if (ref->deletion) { + delete_ref(rs.dst, NULL, 0); + } else + update_ref("update by push", rs.dst, + ref->new_sha1, NULL, 0, 0); + free(rs.dst); + } +} + +#define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3) + +static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg) +{ + fprintf(stderr, " %c %-*s ", flag, SUMMARY_WIDTH, summary); + if (from) + fprintf(stderr, "%s -> %s", prettify_ref(from), prettify_ref(to)); + else + fputs(prettify_ref(to), stderr); + if (msg) { + fputs(" (", stderr); + fputs(msg, stderr); + fputc(')', stderr); + } + fputc('\n', stderr); +} + +static const char *status_abbrev(unsigned char sha1[20]) +{ + return find_unique_abbrev(sha1, DEFAULT_ABBREV); +} + +static void print_ok_ref_status(struct ref *ref) +{ + if (ref->deletion) + print_ref_status('-', "[deleted]", ref, NULL, NULL); + else if (is_null_sha1(ref->old_sha1)) + print_ref_status('*', + (!prefixcmp(ref->name, "refs/tags/") ? "[new tag]" : + "[new branch]"), + ref, ref->peer_ref, NULL); + else { + char quickref[84]; + char type; + const char *msg; + + strcpy(quickref, status_abbrev(ref->old_sha1)); + if (ref->nonfastforward) { + strcat(quickref, "..."); + type = '+'; + msg = "forced update"; + } else { + strcat(quickref, ".."); + type = ' '; + msg = NULL; + } + strcat(quickref, status_abbrev(ref->new_sha1)); + + print_ref_status(type, quickref, ref, ref->peer_ref, msg); + } +} + +static int print_one_push_status(struct ref *ref, const char *dest, int count) +{ + if (!count) + fprintf(stderr, "To %s\n", dest); + + switch(ref->status) { + case REF_STATUS_NONE: + print_ref_status('X', "[no match]", ref, NULL, NULL); + break; + case REF_STATUS_REJECT_NODELETE: + print_ref_status('!', "[rejected]", ref, NULL, + "remote does not support deleting refs"); + break; + case REF_STATUS_UPTODATE: + print_ref_status('=', "[up to date]", ref, + ref->peer_ref, NULL); + break; + case REF_STATUS_REJECT_NONFASTFORWARD: + print_ref_status('!', "[rejected]", ref, ref->peer_ref, + "non-fast forward"); + break; + case REF_STATUS_REMOTE_REJECT: + print_ref_status('!', "[remote rejected]", ref, + ref->deletion ? NULL : ref->peer_ref, + ref->remote_status); + break; + case REF_STATUS_EXPECTING_REPORT: + print_ref_status('!', "[remote failure]", ref, + ref->deletion ? NULL : ref->peer_ref, + "remote failed to report status"); + break; + case REF_STATUS_OK: + print_ok_ref_status(ref); + break; + } + + return 1; +} + +static void print_push_status(const char *dest, struct ref *refs, int verbose) +{ + struct ref *ref; + int n = 0; + + if (verbose) { + for (ref = refs; ref; ref = ref->next) + if (ref->status == REF_STATUS_UPTODATE) + n += print_one_push_status(ref, dest, n); + } + + for (ref = refs; ref; ref = ref->next) + if (ref->status == REF_STATUS_OK) + n += print_one_push_status(ref, dest, n); + + for (ref = refs; ref; ref = ref->next) { + if (ref->status != REF_STATUS_NONE && + ref->status != REF_STATUS_UPTODATE && + ref->status != REF_STATUS_OK) + n += print_one_push_status(ref, dest, n); + } +} + +static void verify_remote_names(int nr_heads, const char **heads) +{ + int i; + + for (i = 0; i < nr_heads; i++) { + const char *local = heads[i]; + const char *remote = strrchr(heads[i], ':'); + + if (*local == '+') + local++; + + /* A matching refspec is okay. */ + if (remote == local && remote[1] == '\0') + continue; + + remote = remote ? (remote + 1) : local; + switch (check_ref_format(remote)) { + case 0: /* ok */ + case CHECK_REF_FORMAT_ONELEVEL: + /* ok but a single level -- that is fine for + * a match pattern. + */ + case CHECK_REF_FORMAT_WILDCARD: + /* ok but ends with a pattern-match character */ + continue; + } + die("remote part of refspec is not a valid name in %s", + heads[i]); + } +} + +static int git_transport_push(struct transport *transport, struct ref *remote_refs, int flags) { struct git_transport_data *data = transport->data; struct send_pack_args args; + int ret; + + if (!data->conn) { + struct ref *tmp_refs; + connect_setup(transport, 1, 0); + + get_remote_heads(data->fd[0], &tmp_refs, 0, NULL, REF_NORMAL, + NULL); + } - args.receivepack = data->receivepack; - args.send_all = !!(flags & TRANSPORT_PUSH_ALL); args.send_mirror = !!(flags & TRANSPORT_PUSH_MIRROR); args.force_update = !!(flags & TRANSPORT_PUSH_FORCE); args.use_thin_pack = data->thin; args.verbose = !!(flags & TRANSPORT_PUSH_VERBOSE); args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN); - return send_pack(&args, transport->url, transport->remote, refspec_nr, refspec); + ret = send_pack(&args, data->fd, data->conn, remote_refs, + &data->extra_have); + + close(data->fd[1]); + close(data->fd[0]); + ret |= finish_connect(data->conn); + data->conn = NULL; + + return ret; } static int disconnect_git(struct transport *transport) @@ -753,7 +962,7 @@ struct transport *transport_get(struct remote *remote, const char *url) ret->set_option = set_git_option; ret->get_refs_list = get_refs_via_connect; ret->fetch = fetch_refs_via_pack; - ret->push = git_transport_push; + ret->push_refs = git_transport_push; ret->disconnect = disconnect_git; data->thin = 1; @@ -780,15 +989,53 @@ int transport_set_option(struct transport *transport, int transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) { - if (!transport->push) - return 1; - return transport->push(transport, refspec_nr, refspec, flags); + verify_remote_names(refspec_nr, refspec); + + if (transport->push) + return transport->push(transport, refspec_nr, refspec, flags); + if (transport->push_refs) { + struct ref *remote_refs = + transport->get_refs_list(transport, 1); + struct ref **remote_tail; + struct ref *local_refs = get_local_heads(); + int match_flags = MATCH_REFS_NONE; + int verbose = flags & TRANSPORT_PUSH_VERBOSE; + int ret; + + if (flags & TRANSPORT_PUSH_ALL) + match_flags |= MATCH_REFS_ALL; + if (flags & TRANSPORT_PUSH_MIRROR) + match_flags |= MATCH_REFS_MIRROR; + + remote_tail = &remote_refs; + while (*remote_tail) + remote_tail = &((*remote_tail)->next); + if (match_refs(local_refs, remote_refs, &remote_tail, + refspec_nr, refspec, match_flags)) { + return -1; + } + + ret = transport->push_refs(transport, remote_refs, flags); + + print_push_status(transport->url, remote_refs, verbose); + + if (!(flags & TRANSPORT_PUSH_DRY_RUN)) { + struct ref *ref; + for (ref = remote_refs; ref; ref = ref->next) + update_tracking_ref(transport->remote, ref, verbose); + } + + if (!ret && !refs_pushed(remote_refs)) + fprintf(stderr, "Everything up-to-date\n"); + return ret; + } + return 1; } const struct ref *transport_get_remote_refs(struct transport *transport) { if (!transport->remote_refs) - transport->remote_refs = transport->get_refs_list(transport); + transport->remote_refs = transport->get_refs_list(transport, 0); return transport->remote_refs; } diff --git a/transport.h b/transport.h index 6bbc1a8264..b1c2252766 100644 --- a/transport.h +++ b/transport.h @@ -18,8 +18,9 @@ struct transport { int (*set_option)(struct transport *connection, const char *name, const char *value); - struct ref *(*get_refs_list)(struct transport *transport); + struct ref *(*get_refs_list)(struct transport *transport, int for_push); int (*fetch)(struct transport *transport, int refs_nr, const struct ref **refs); + int (*push_refs)(struct transport *transport, struct ref *refs, int flags); int (*push)(struct transport *connection, int refspec_nr, const char **refspec, int flags); int (*disconnect)(struct transport *connection); From 4251ccbd80eff92270503a48d35385516c3d106d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 9 Mar 2009 18:47:29 -0700 Subject: [PATCH 072/289] http.c: style cleanups Signed-off-by: Junio C Hamano --- http.c | 44 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/http.c b/http.c index 56f18f1b03..d1ead6643d 100644 --- a/http.c +++ b/http.c @@ -1,7 +1,7 @@ #include "http.h" int data_received; -int active_requests = 0; +int active_requests; #ifdef USE_CURL_MULTI static int max_requests = -1; @@ -13,22 +13,22 @@ static CURL *curl_default; char curl_errorstr[CURL_ERROR_SIZE]; static int curl_ssl_verify = -1; -static const char *ssl_cert = NULL; +static const char *ssl_cert; #if LIBCURL_VERSION_NUM >= 0x070902 -static const char *ssl_key = NULL; +static const char *ssl_key; #endif #if LIBCURL_VERSION_NUM >= 0x070908 -static const char *ssl_capath = NULL; +static const char *ssl_capath; #endif -static const char *ssl_cainfo = NULL; +static const char *ssl_cainfo; static long curl_low_speed_limit = -1; static long curl_low_speed_time = -1; -static int curl_ftp_no_epsv = 0; -static const char *curl_http_proxy = NULL; +static int curl_ftp_no_epsv; +static const char *curl_http_proxy; static struct curl_slist *pragma_header; -static struct active_request_slot *active_queue_head = NULL; +static struct active_request_slot *active_queue_head; size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb, void *buffer_) { @@ -94,9 +94,8 @@ static void process_curl_messages(void) static int http_options(const char *var, const char *value, void *cb) { if (!strcmp("http.sslverify", var)) { - if (curl_ssl_verify == -1) { + if (curl_ssl_verify == -1) curl_ssl_verify = git_config_bool(var, value); - } return 0; } @@ -158,9 +157,9 @@ static int http_options(const char *var, const char *value, void *cb) return git_default_config(var, value, cb); } -static CURL* get_curl_handle(void) +static CURL *get_curl_handle(void) { - CURL* result = curl_easy_init(); + CURL *result = curl_easy_init(); if (!curl_ssl_verify) { curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 0); @@ -322,15 +321,14 @@ struct active_request_slot *get_active_slot(void) /* Wait for a slot to open up if the queue is full */ while (active_requests >= max_requests) { curl_multi_perform(curlm, &num_transfers); - if (num_transfers < active_requests) { + if (num_transfers < active_requests) process_curl_messages(); - } } #endif - while (slot != NULL && slot->in_use) { + while (slot != NULL && slot->in_use) slot = slot->next; - } + if (slot == NULL) { newslot = xmalloc(sizeof(*newslot)); newslot->curl = NULL; @@ -341,9 +339,8 @@ struct active_request_slot *get_active_slot(void) if (slot == NULL) { active_queue_head = newslot; } else { - while (slot->next != NULL) { + while (slot->next != NULL) slot = slot->next; - } slot->next = newslot; } slot = newslot; @@ -404,7 +401,7 @@ struct fill_chain { struct fill_chain *next; }; -static struct fill_chain *fill_cfg = NULL; +static struct fill_chain *fill_cfg; void add_fill_function(void *data, int (*fill)(void *)) { @@ -535,9 +532,8 @@ static void finish_active_slot(struct active_request_slot *slot) } /* Run callback if appropriate */ - if (slot->callback_func != NULL) { + if (slot->callback_func != NULL) slot->callback_func(slot->callback_data); - } } void finish_all_active_slots(void) @@ -567,8 +563,10 @@ static inline int needs_quote(int ch) static inline int hex(int v) { - if (v < 10) return '0' + v; - else return 'A' + v - 10; + if (v < 10) + return '0' + v; + else + return 'A' + v - 10; } static char *quote_ref_url(const char *base, const char *ref) From 72f600832f75db626fd9290a21d02d49c92ca9ca Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 10 Mar 2009 01:20:42 -0700 Subject: [PATCH 073/289] Improve "git branch --tracking" output An earlier patch always spelled the full name of the ref that we track (e.g. "refs/heads/frotz" instead of just "frotz" when we mean the branch whose name is "frotz"). Worse yet, because we now use the true name of the ref at the original repository when talk about a tracking branch that copies from a remote, such a full name alone still does not give enough information. This reorganizes the verbose codepath to: - differentiate "refs/heads/something" and everything else; we say that the branch tracks "branch " if it begins with "refs/heads/", and otherwise the branch tracks "ref refs//"; - report the name of the remote when we talk about a tracking branch, by saying "branch frotz from origin"; - not say "by merging" at the end; it is the default and is not worth reporting. Signed-off-by: Junio C Hamano Signed-off-by: Junio C Hamano --- branch.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/branch.c b/branch.c index d20fb0490b..5f889fee6b 100644 --- a/branch.c +++ b/branch.c @@ -65,12 +65,23 @@ void install_branch_config(int flag, const char *local, const char *origin, cons git_config_set(key.buf, "true"); } - if (flag & BRANCH_CONFIG_VERBOSE) - printf("Branch %s set up to track %s branch %s %s.\n", - local, - origin ? "remote" : "local", - remote, - rebasing ? "by rebasing" : "by merging"); + if (flag & BRANCH_CONFIG_VERBOSE) { + strbuf_reset(&key); + + strbuf_addstr(&key, origin ? "remote" : "local"); + + /* Are we tracking a proper "branch"? */ + if (!prefixcmp(remote, "refs/heads/")) { + strbuf_addf(&key, " branch %s", remote + 11); + if (origin) + strbuf_addf(&key, " from %s", origin); + } + else + strbuf_addf(&key, " ref %s", remote); + printf("Branch %s set up to track %s%s.\n", + local, key.buf, + rebasing ? " by rebasing" : ""); + } strbuf_release(&key); } From 7059cd99fc671f9594b61cee7d10d69704f3ebe2 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 9 Mar 2009 19:00:30 -0700 Subject: [PATCH 074/289] http_init(): Fix config file parsing We honor the command line options, environment variables, variables in repository configuration file, variables in user's global configuration file, variables in the system configuration file, and then finally use built-in default. To implement this semantics, the code should: - start from built-in default values; - call git_config() with the configuration parser callback, which implements "later definition overrides earlier ones" logic (git_config() reads the system's, user's and then repository's configuration file in this order); - override the result from the above with environment variables if set; - override the result from the above with command line options. The initialization code http_init() for http transfer got this wrong, and implemented a "first one wins, ignoring the later ones" in http_options(), to compensate this mistake, read environment variables before calling git_config(). This is all wrong. As a second class citizen, the http codepath hasn't been audited as closely as other parts of the system, but we should try to bring sanity to it, before inviting contributors to improve on it. Signed-off-by: Junio C Hamano --- http.c | 69 +++++++++++++++++++++++----------------------------------- 1 file changed, 27 insertions(+), 42 deletions(-) diff --git a/http.c b/http.c index d1ead6643d..4c4614c92f 100644 --- a/http.c +++ b/http.c @@ -94,52 +94,33 @@ static void process_curl_messages(void) static int http_options(const char *var, const char *value, void *cb) { if (!strcmp("http.sslverify", var)) { - if (curl_ssl_verify == -1) - curl_ssl_verify = git_config_bool(var, value); - return 0; - } - - if (!strcmp("http.sslcert", var)) { - if (ssl_cert == NULL) - return git_config_string(&ssl_cert, var, value); + curl_ssl_verify = git_config_bool(var, value); return 0; } + if (!strcmp("http.sslcert", var)) + return git_config_string(&ssl_cert, var, value); #if LIBCURL_VERSION_NUM >= 0x070902 - if (!strcmp("http.sslkey", var)) { - if (ssl_key == NULL) - return git_config_string(&ssl_key, var, value); - return 0; - } + if (!strcmp("http.sslkey", var)) + return git_config_string(&ssl_key, var, value); #endif #if LIBCURL_VERSION_NUM >= 0x070908 - if (!strcmp("http.sslcapath", var)) { - if (ssl_capath == NULL) - return git_config_string(&ssl_capath, var, value); - return 0; - } + if (!strcmp("http.sslcapath", var)) + return git_config_string(&ssl_capath, var, value); #endif - if (!strcmp("http.sslcainfo", var)) { - if (ssl_cainfo == NULL) - return git_config_string(&ssl_cainfo, var, value); - return 0; - } - + if (!strcmp("http.sslcainfo", var)) + return git_config_string(&ssl_cainfo, var, value); #ifdef USE_CURL_MULTI if (!strcmp("http.maxrequests", var)) { - if (max_requests == -1) - max_requests = git_config_int(var, value); + max_requests = git_config_int(var, value); return 0; } #endif - if (!strcmp("http.lowspeedlimit", var)) { - if (curl_low_speed_limit == -1) - curl_low_speed_limit = (long)git_config_int(var, value); + curl_low_speed_limit = (long)git_config_int(var, value); return 0; } if (!strcmp("http.lowspeedtime", var)) { - if (curl_low_speed_time == -1) - curl_low_speed_time = (long)git_config_int(var, value); + curl_low_speed_time = (long)git_config_int(var, value); return 0; } @@ -147,11 +128,8 @@ static int http_options(const char *var, const char *value, void *cb) curl_ftp_no_epsv = git_config_bool(var, value); return 0; } - if (!strcmp("http.proxy", var)) { - if (curl_http_proxy == NULL) - return git_config_string(&curl_http_proxy, var, value); - return 0; - } + if (!strcmp("http.proxy", var)) + return git_config_string(&curl_http_proxy, var, value); /* Fall back on the default ones */ return git_default_config(var, value, cb); @@ -212,11 +190,20 @@ static CURL *get_curl_handle(void) return result; } +static void set_from_env(const char **var, const char *envname) +{ + const char *val = getenv(envname); + if (val) + *var = val; +} + void http_init(struct remote *remote) { char *low_speed_limit; char *low_speed_time; + git_config(http_options, NULL); + curl_global_init(CURL_GLOBAL_ALL); if (remote && remote->http_proxy) @@ -241,14 +228,14 @@ void http_init(struct remote *remote) if (getenv("GIT_SSL_NO_VERIFY")) curl_ssl_verify = 0; - ssl_cert = getenv("GIT_SSL_CERT"); + set_from_env(&ssl_cert, "GIT_SSL_CERT"); #if LIBCURL_VERSION_NUM >= 0x070902 - ssl_key = getenv("GIT_SSL_KEY"); + set_from_env(&ssl_key, "GIT_SSL_KEY"); #endif #if LIBCURL_VERSION_NUM >= 0x070908 - ssl_capath = getenv("GIT_SSL_CAPATH"); + set_from_env(&ssl_capath, "GIT_SSL_CAPATH"); #endif - ssl_cainfo = getenv("GIT_SSL_CAINFO"); + set_from_env(&ssl_cainfo, "GIT_SSL_CAINFO"); low_speed_limit = getenv("GIT_HTTP_LOW_SPEED_LIMIT"); if (low_speed_limit != NULL) @@ -257,8 +244,6 @@ void http_init(struct remote *remote) if (low_speed_time != NULL) curl_low_speed_time = strtol(low_speed_time, NULL, 10); - git_config(http_options, NULL); - if (curl_ssl_verify == -1) curl_ssl_verify = 1; From c33976cbc6d1895fca5c1683fba678e786ee3e58 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 9 Mar 2009 23:34:25 -0700 Subject: [PATCH 075/289] http authentication via prompts Curl is designed not to ask for password when only username is given in the URL, but has a way for application to feed a (username, password) pair to it. With this patch, you do not have to keep your password in plaintext in your $HOME/.netrc file when talking with a password protected URL with http://@/path/to/repository.git/ syntax. The code handles only the http-walker side, not the push side. At least, not yet. But interested parties can add support for it. Signed-off-by: Junio C Hamano --- http.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/http.c b/http.c index 4c4614c92f..b8f947e405 100644 --- a/http.c +++ b/http.c @@ -25,6 +25,7 @@ static long curl_low_speed_limit = -1; static long curl_low_speed_time = -1; static int curl_ftp_no_epsv; static const char *curl_http_proxy; +static char *user_name, *user_pass; static struct curl_slist *pragma_header; @@ -135,6 +136,20 @@ static int http_options(const char *var, const char *value, void *cb) return git_default_config(var, value, cb); } +static void init_curl_http_auth(CURL *result) +{ + if (!user_name) + curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); + else { + struct strbuf up = STRBUF_INIT; + if (!user_pass) + user_pass = xstrdup(getpass("Password: ")); + strbuf_addf(&up, "%s:%s", user_name, user_pass); + curl_easy_setopt(result, CURLOPT_USERPWD, + strbuf_detach(&up, NULL)); + } +} + static CURL *get_curl_handle(void) { CURL *result = curl_easy_init(); @@ -153,6 +168,8 @@ static CURL *get_curl_handle(void) curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); #endif + init_curl_http_auth(result); + if (ssl_cert != NULL) curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert); #if LIBCURL_VERSION_NUM >= 0x070902 @@ -190,6 +207,46 @@ static CURL *get_curl_handle(void) return result; } +static void http_auth_init(const char *url) +{ + char *at, *colon, *cp, *slash; + int len; + + cp = strstr(url, "://"); + if (!cp) + return; + + /* + * Ok, the URL looks like "proto://something". Which one? + * "proto://:@/...", + * "proto://@/...", or just + * "proto:///..."? + */ + cp += 3; + at = strchr(cp, '@'); + colon = strchr(cp, ':'); + slash = strchrnul(cp, '/'); + if (!at || slash <= at) + return; /* No credentials */ + if (!colon || at <= colon) { + /* Only username */ + len = at - cp; + user_name = xmalloc(len + 1); + memcpy(user_name, cp, len); + user_name[len] = '\0'; + user_pass = NULL; + } else { + len = colon - cp; + user_name = xmalloc(len + 1); + memcpy(user_name, cp, len); + user_name[len] = '\0'; + len = at - (colon + 1); + user_pass = xmalloc(len + 1); + memcpy(user_pass, colon + 1, len); + user_pass[len] = '\0'; + } +} + static void set_from_env(const char **var, const char *envname) { const char *val = getenv(envname); @@ -255,6 +312,9 @@ void http_init(struct remote *remote) if (getenv("GIT_CURL_FTP_NO_EPSV")) curl_ftp_no_epsv = 1; + if (remote && remote->url && remote->url[0]) + http_auth_init(remote->url[0]); + #ifndef NO_CURL_EASY_DUPHANDLE curl_default = get_curl_handle(); #endif From fa685bdf45cbaa997255cc78a23494b995e9769a Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Wed, 11 Mar 2009 01:47:20 -0400 Subject: [PATCH 076/289] Give error when no remote is configured When there's no explicitly-named remote, we use the remote specified for the current branch, which in turn defaults to "origin". But it this case should require the remote to actually be configured, and not fall back to the path "origin". Possibly, the config file's "remote = something" should require the something to be a configured remote instead of a bare repository URL, but we actually test with a bare repository URL. In fetch, we were giving the sensible error message when coming up with a URL failed, but this wasn't actually reachable, so move that error up and use it when appropriate. In push, we need a new error message, because the old one (formerly unreachable without a lot of help) used the repo name, which was NULL. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- builtin-fetch.c | 6 +++--- builtin-push.c | 7 +++++-- remote.c | 30 +++++++++++++++++++++++++++--- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/builtin-fetch.c b/builtin-fetch.c index 1e4a3d9c51..7fb35fca9d 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -636,6 +636,9 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) else remote = remote_get(argv[0]); + if (!remote) + die("Where do you want to fetch from today?"); + transport = transport_get(remote, remote->url[0]); if (verbosity >= 2) transport->verbose = 1; @@ -648,9 +651,6 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) if (depth) set_option(TRANS_OPT_DEPTH, depth); - if (!transport->url) - die("Where do you want to fetch from today?"); - if (argc > 1) { int j = 0; refs = xcalloc(argc + 1, sizeof(const char *)); diff --git a/builtin-push.c b/builtin-push.c index 122fdcfbdc..ca36fb1e58 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -53,8 +53,11 @@ static int do_push(const char *repo, int flags) int i, errs; struct remote *remote = remote_get(repo); - if (!remote) - die("bad repository '%s'", repo); + if (!remote) { + if (repo) + die("bad repository '%s'", repo); + die("No destination configured to push to."); + } if (remote->mirror) flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE); diff --git a/remote.c b/remote.c index d7079c6dd8..199830ea93 100644 --- a/remote.c +++ b/remote.c @@ -38,6 +38,7 @@ static int branches_nr; static struct branch *current_branch; static const char *default_remote_name; +static int explicit_default_remote_name; static struct rewrite **rewrite; static int rewrite_alloc; @@ -104,6 +105,16 @@ static void add_url_alias(struct remote *remote, const char *url) add_url(remote, alias_url(url)); } +static struct remote *get_remote_by_name(const char *name) +{ + int i; + for (i = 0; i < remotes_nr; i++) { + if (!strcmp(name, remotes[i]->name)) + return remotes[i]; + } + return NULL; +} + static struct remote *make_remote(const char *name, int len) { struct remote *ret; @@ -330,8 +341,10 @@ static int handle_config(const char *key, const char *value, void *cb) if (!value) return config_error_nonbool(key); branch->remote_name = xstrdup(value); - if (branch == current_branch) + if (branch == current_branch) { default_remote_name = branch->remote_name; + explicit_default_remote_name = 1; + } } else if (!strcmp(subkey, ".merge")) { if (!value) return config_error_nonbool(key); @@ -643,11 +656,22 @@ static int valid_remote_nick(const char *name) struct remote *remote_get(const char *name) { struct remote *ret; + int name_given = 0; read_config(); - if (!name) + if (name) + name_given = 1; + else { name = default_remote_name; - ret = make_remote(name, 0); + name_given = explicit_default_remote_name; + } + if (name_given) + ret = make_remote(name, 0); + else { + ret = get_remote_by_name(name); + if (!ret) + return NULL; + } if (valid_remote_nick(name)) { if (!ret->url) read_remotes_file(ret); From 7efaeba2a8006bb52712eb90c6fbc736b9632cab Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 9 Mar 2009 19:44:55 +0100 Subject: [PATCH 077/289] rsync transport: allow local paths, and fix tests Earlier, the rsync tests were disabled by default, as they needed a running rsyncd daemon. This was only due to the limitation that our rsync transport only allowed full URLs of the form rsync:/// Relaxing the URLs to allow rsync: permitted the change in the tests to run whenever rsync is available, without requiring a fully configured and running rsyncd. While at it, the tests were fixed so that they run in directories with a space in their name. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- t/t5510-fetch.sh | 39 ++++++++++++++++++++------------------- transport.c | 23 +++++++++++++++-------- 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 9e679b402d..bee3424fed 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -191,38 +191,39 @@ test_expect_success 'bundle should be able to create a full history' ' ' -test "$TEST_RSYNC" && { +! rsync --help > /dev/null 2> /dev/null && +say 'Skipping rsync tests because rsync was not found' || { test_expect_success 'fetch via rsync' ' git pack-refs && mkdir rsynced && - cd rsynced && - git init && - git fetch rsync://127.0.0.1$(pwd)/../.git master:refs/heads/master && - git gc --prune && - test $(git rev-parse master) = $(cd .. && git rev-parse master) && - git fsck --full + (cd rsynced && + git init --bare && + git fetch "rsync:$(pwd)/../.git" master:refs/heads/master && + git gc --prune && + test $(git rev-parse master) = $(cd .. && git rev-parse master) && + git fsck --full) ' test_expect_success 'push via rsync' ' - mkdir ../rsynced2 && - (cd ../rsynced2 && + mkdir rsynced2 && + (cd rsynced2 && git init) && - git push rsync://127.0.0.1$(pwd)/../rsynced2/.git master && - cd ../rsynced2 && - git gc --prune && - test $(git rev-parse master) = $(cd .. && git rev-parse master) && - git fsck --full + (cd rsynced && + git push "rsync:$(pwd)/../rsynced2/.git" master) && + (cd rsynced2 && + git gc --prune && + test $(git rev-parse master) = $(cd .. && git rev-parse master) && + git fsck --full) ' test_expect_success 'push via rsync' ' - cd .. && mkdir rsynced3 && (cd rsynced3 && git init) && - git push --all rsync://127.0.0.1$(pwd)/rsynced3/.git && - cd rsynced3 && - test $(git rev-parse master) = $(cd .. && git rev-parse master) && - git fsck --full + git push --all "rsync:$(pwd)/rsynced3/.git" && + (cd rsynced3 && + test $(git rev-parse master) = $(cd .. && git rev-parse master) && + git fsck --full) ' } diff --git a/transport.c b/transport.c index 9ad4a16c31..9ae92cd39c 100644 --- a/transport.c +++ b/transport.c @@ -138,6 +138,11 @@ static void insert_packed_refs(const char *packed_refs, struct ref **list) } } +static const char *rsync_url(const char *url) +{ + return prefixcmp(url, "rsync://") ? skip_prefix(url, "rsync:") : url; +} + static struct ref *get_refs_via_rsync(struct transport *transport) { struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT; @@ -153,7 +158,7 @@ static struct ref *get_refs_via_rsync(struct transport *transport) die ("Could not make temporary directory"); temp_dir_len = temp_dir.len; - strbuf_addstr(&buf, transport->url); + strbuf_addstr(&buf, rsync_url(transport->url)); strbuf_addstr(&buf, "/refs"); memset(&rsync, 0, sizeof(rsync)); @@ -169,7 +174,7 @@ static struct ref *get_refs_via_rsync(struct transport *transport) die ("Could not run rsync to get refs"); strbuf_reset(&buf); - strbuf_addstr(&buf, transport->url); + strbuf_addstr(&buf, rsync_url(transport->url)); strbuf_addstr(&buf, "/packed-refs"); args[2] = buf.buf; @@ -206,7 +211,7 @@ static int fetch_objs_via_rsync(struct transport *transport, const char *args[8]; int result; - strbuf_addstr(&buf, transport->url); + strbuf_addstr(&buf, rsync_url(transport->url)); strbuf_addstr(&buf, "/objects/"); memset(&rsync, 0, sizeof(rsync)); @@ -285,7 +290,7 @@ static int rsync_transport_push(struct transport *transport, /* first push the objects */ - strbuf_addstr(&buf, transport->url); + strbuf_addstr(&buf, rsync_url(transport->url)); strbuf_addch(&buf, '/'); memset(&rsync, 0, sizeof(rsync)); @@ -306,7 +311,8 @@ static int rsync_transport_push(struct transport *transport, args[i++] = NULL; if (run_command(&rsync)) - return error("Could not push objects to %s", transport->url); + return error("Could not push objects to %s", + rsync_url(transport->url)); /* copy the refs to the temporary directory; they could be packed. */ @@ -327,10 +333,11 @@ static int rsync_transport_push(struct transport *transport, if (!(flags & TRANSPORT_PUSH_FORCE)) args[i++] = "--ignore-existing"; args[i++] = temp_dir.buf; - args[i++] = transport->url; + args[i++] = rsync_url(transport->url); args[i++] = NULL; if (run_command(&rsync)) - result = error("Could not push to %s", transport->url); + result = error("Could not push to %s", + rsync_url(transport->url)); if (remove_dir_recursively(&temp_dir, 0)) warning ("Could not remove temporary directory %s.", @@ -723,7 +730,7 @@ struct transport *transport_get(struct remote *remote, const char *url) ret->remote = remote; ret->url = url; - if (!prefixcmp(url, "rsync://")) { + if (!prefixcmp(url, "rsync:")) { ret->get_refs_list = get_refs_via_rsync; ret->fetch = fetch_objs_via_rsync; ret->push = rsync_transport_push; From 34df8abaf358c83cc1447d0a81bda7848685a1c9 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 10 Mar 2009 22:54:17 +0100 Subject: [PATCH 078/289] recv_sideband: Bands #2 and #3 always go to stderr This removes the last parameter of recv_sideband, by which the callers told which channel bands #2 and #3 should be written to. Sayeth Shawn Pearce: The definition of the streams in the current sideband protocol are rather well defined for the one protocol that uses it, fetch-pack/receive-pack: stream #1: pack data stream #2: stderr messages, progress, meant for tty stream #3: abort message, remote is dead, goodbye! Since both callers of the function passed 2 for the parameter, we hereby remove it and send bands #2 and #3 to stderr explicitly using fprintf. This has the nice side-effect that these two streams pass through our ANSI emulation layer on Windows. Signed-off-by: Johannes Sixt Acked-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-archive.c | 2 +- builtin-fetch-pack.c | 2 +- sideband.c | 19 ++++++++----------- sideband.h | 2 +- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/builtin-archive.c b/builtin-archive.c index 60adef9363..ab50cebba0 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -52,7 +52,7 @@ static int run_remote_archiver(int argc, const char **argv, die("git archive: expected a flush"); /* Now, start reading from fd[0] and spit it out to stdout */ - rv = recv_sideband("archive", fd[0], 1, 2); + rv = recv_sideband("archive", fd[0], 1); close(fd[0]); close(fd[1]); rv |= finish_connect(conn); diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c index c2e5adc884..2b360994bf 100644 --- a/builtin-fetch-pack.c +++ b/builtin-fetch-pack.c @@ -482,7 +482,7 @@ static int sideband_demux(int fd, void *data) { int *xd = data; - return recv_sideband("fetch-pack", xd[0], fd, 2); + return recv_sideband("fetch-pack", xd[0], fd); } static int get_pack(int xd[2], char **pack_lockfile) diff --git a/sideband.c b/sideband.c index cca3360546..899b1ff366 100644 --- a/sideband.c +++ b/sideband.c @@ -19,7 +19,7 @@ #define FIX_SIZE 10 /* large enough for any of the above */ -int recv_sideband(const char *me, int in_stream, int out, int err) +int recv_sideband(const char *me, int in_stream, int out) { unsigned pf = strlen(PREFIX); unsigned sf; @@ -41,8 +41,7 @@ int recv_sideband(const char *me, int in_stream, int out, int err) if (len == 0) break; if (len < 1) { - len = sprintf(buf, "%s: protocol error: no band designator\n", me); - safe_write(err, buf, len); + fprintf(stderr, "%s: protocol error: no band designator\n", me); return SIDEBAND_PROTOCOL_ERROR; } band = buf[pf] & 0xff; @@ -50,8 +49,8 @@ int recv_sideband(const char *me, int in_stream, int out, int err) switch (band) { case 3: buf[pf] = ' '; - buf[pf+1+len] = '\n'; - safe_write(err, buf, pf+1+len+1); + buf[pf+1+len] = '\0'; + fprintf(stderr, "%s\n", buf); return SIDEBAND_REMOTE_ERROR; case 2: buf[pf] = ' '; @@ -95,12 +94,12 @@ int recv_sideband(const char *me, int in_stream, int out, int err) memcpy(save, b + brk, sf); b[brk + sf - 1] = b[brk - 1]; memcpy(b + brk - 1, suffix, sf); - safe_write(err, b, brk + sf); + fprintf(stderr, "%.*s", brk + sf, b); memcpy(b + brk, save, sf); len -= brk; } else { int l = brk ? brk : len; - safe_write(err, b, l); + fprintf(stderr, "%.*s", l, b); len -= l; } @@ -112,10 +111,8 @@ int recv_sideband(const char *me, int in_stream, int out, int err) safe_write(out, buf + pf+1, len); continue; default: - len = sprintf(buf, - "%s: protocol error: bad band #%d\n", - me, band); - safe_write(err, buf, len); + fprintf(stderr, "%s: protocol error: bad band #%d\n", + me, band); return SIDEBAND_PROTOCOL_ERROR; } } diff --git a/sideband.h b/sideband.h index a84b6917c7..d72db35d1e 100644 --- a/sideband.h +++ b/sideband.h @@ -7,7 +7,7 @@ #define DEFAULT_PACKET_MAX 1000 #define LARGE_PACKET_MAX 65520 -int recv_sideband(const char *me, int in_stream, int out, int err); +int recv_sideband(const char *me, int in_stream, int out); ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max); #endif From 1897713fbd96229f3581b8e8a5653da917882195 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 10 Mar 2009 22:58:09 +0100 Subject: [PATCH 079/289] winansi: support ESC [ K (erase in line) Signed-off-by: Johannes Schindelin Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- compat/winansi.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/compat/winansi.c b/compat/winansi.c index e2d96dfe6f..44dc293ad3 100644 --- a/compat/winansi.c +++ b/compat/winansi.c @@ -18,8 +18,6 @@ This file is git-specific. Therefore, this file does not attempt to implement any codes that are not used by git. - - TODO: K */ static HANDLE console; @@ -79,6 +77,20 @@ static void set_console_attr(void) SetConsoleTextAttribute(console, attributes); } +static void erase_in_line(void) +{ + CONSOLE_SCREEN_BUFFER_INFO sbi; + + if (!console) + return; + + GetConsoleScreenBufferInfo(console, &sbi); + FillConsoleOutputCharacterA(console, ' ', + sbi.dwSize.X - sbi.dwCursorPosition.X, sbi.dwCursorPosition, + NULL); +} + + static const char *set_attr(const char *str) { const char *func; @@ -218,7 +230,7 @@ static const char *set_attr(const char *str) set_console_attr(); break; case 'K': - /* TODO */ + erase_in_line(); break; default: /* Unsupported code */ From aaab4b9fb97c1f638d02be7b8c432a4d57f37c56 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Wed, 11 Mar 2009 23:40:13 +0100 Subject: [PATCH 080/289] send-email: test --no-thread --in-reply-to combination 3e0c4ff (send-email: respect in-reply-to regardless of threading, 2009-03-01) fixed the handling of the In-Reply-To header when both --no-thread and --in-reply-to are in effect. Add a test for it. Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano --- t/t9001-send-email.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index d098a01ba3..a404204b17 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -292,4 +292,15 @@ test_expect_success '--compose adds MIME for utf8 subject' ' grep "^Subject: =?utf-8?q?utf8-s=C3=BCbj=C3=ABct?=" msgtxt1 ' +test_expect_success 'in-reply-to but no threading' ' + git send-email \ + --dry-run \ + --from="Example " \ + --to=nobody@example.com \ + --in-reply-to="" \ + --no-thread \ + $patches | + grep "In-Reply-To: " +' + test_done From aab3b9a1aa3b7d082088ab014480b5d81c437624 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 12 Mar 2009 00:02:12 -0700 Subject: [PATCH 081/289] read-tree A B C: do not create a bogus index and do not segfault "git read-tree A B C..." without the "-m" (merge) option is a way to read these trees on top of each other to get an overlay of them. An ancient commit ee6566e (Rewrite read-tree, 2005-09-05) passed the ADD_CACHE_SKIP_DFCHECK flag when calling add_index_entry() to add the paths obtained from these trees to the index, but it is an incorrect use of the flag. The flag is meant to be used by callers who know the addition of the entry does not introduce a D/F conflict to the index in order to avoid the overhead of checking. This bug resulted in a bogus index that records both "x" and "x/z" as a blob after reading three trees that have paths ("x"), ("x", "y"), and ("x/z", "y") respectively. 34110cd (Make 'unpack_trees()' have a separate source and destination index, 2008-03-06) refactored the callsites of add_index_entry() incorrectly and added more codepaths that use this flag when it shouldn't be used. Also, 0190457 (Move 'unpack_trees()' over to 'traverse_trees()' interface, 2008-03-05) introduced a bug to call add_index_entry() for the tree that does not have the path in it, passing NULL as a cache entry. This caused reading multiple trees, one of which has path "x" but another doesn't, to segfault. Signed-off-by: Junio C Hamano --- t/t1008-read-tree-overlay.sh | 31 +++++++++++++++++++++++++++++++ unpack-trees.c | 6 +++--- 2 files changed, 34 insertions(+), 3 deletions(-) create mode 100755 t/t1008-read-tree-overlay.sh diff --git a/t/t1008-read-tree-overlay.sh b/t/t1008-read-tree-overlay.sh new file mode 100755 index 0000000000..f9e00285db --- /dev/null +++ b/t/t1008-read-tree-overlay.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +test_description='test multi-tree read-tree without merging' + +. ./test-lib.sh + +test_expect_success setup ' + echo one >a && + git add a && + git commit -m initial && + git tag initial && + echo two >b && + git add b && + git commit -m second && + git checkout -b side initial && + echo three >a && + mkdir b && + echo four >b/c && + git add b/c && + git commit -m third +' + +test_expect_success 'multi-read' ' + git read-tree initial master side && + (echo a; echo b/c) >expect && + git ls-files >actual && + test_cmp expect actual +' + +test_done + diff --git a/unpack-trees.c b/unpack-trees.c index cba0aca062..5b0a8c1728 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -49,7 +49,7 @@ static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce, memcpy(new, ce, size); new->next = NULL; new->ce_flags = (new->ce_flags & ~clear) | set; - add_index_entry(&o->result, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|ADD_CACHE_SKIP_DFCHECK); + add_index_entry(&o->result, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE); } /* Unlink the last component and attempt to remove leading @@ -283,9 +283,9 @@ static int unpack_nondirectories(int n, unsigned long mask, unsigned long dirmas if (o->merge) return call_unpack_fn(src, o); - n += o->merge; for (i = 0; i < n; i++) - add_entry(o, src[i], 0, 0); + if (src[i] && src[i] != o->df_conflict_entry) + add_entry(o, src[i], 0, 0); return 0; } From 750d9305009a0f3fd14c0b5c5e62ae1eb2b18fda Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 12 Mar 2009 22:34:43 -0700 Subject: [PATCH 082/289] http.c: CURLOPT_NETRC_OPTIONAL is not available in ancient versions of cURL Besides, we have already called easy_setopt with the option before coming to this function if it was available, so there is no need to repeat it here. Signed-off-by: Junio C Hamano --- http.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/http.c b/http.c index b8f947e405..2fc55d671e 100644 --- a/http.c +++ b/http.c @@ -138,9 +138,7 @@ static int http_options(const char *var, const char *value, void *cb) static void init_curl_http_auth(CURL *result) { - if (!user_name) - curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); - else { + if (user_name) { struct strbuf up = STRBUF_INIT; if (!user_pass) user_pass = xstrdup(getpass("Password: ")); From e068f4f53b22c64f2058c316d858a892a5e24a6e Mon Sep 17 00:00:00 2001 From: Ben Walton Date: Thu, 12 Mar 2009 15:20:06 -0400 Subject: [PATCH 083/289] configure: ensure settings from user are also usable in the script Allow things set by the user (--with-lib, --with-iconv, etc) to set variables for use by other parts of the script. Display values as they're set. Signed-off-by: Ben Walton Signed-off-by: Junio C Hamano --- configure.ac | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/configure.ac b/configure.ac index 082a03d3cf..0b314d7359 100644 --- a/configure.ac +++ b/configure.ac @@ -42,6 +42,8 @@ else \ if test "$withval" = "yes"; then \ AC_MSG_WARN([You should provide path for --with-$1=PATH]); \ else \ + m4_toupper($1)_PATH=$withval; \ + AC_MSG_NOTICE([Setting m4_toupper($1)_PATH to $withval]); \ GIT_CONF_APPEND_LINE(${PROGRAM}_PATH=$withval); \ fi; \ fi; \ @@ -61,6 +63,8 @@ elif test "$withval" = "yes"; then \ m4_toupper(NO_$1)=; \ else \ m4_toupper(NO_$1)=; \ + m4_toupper($1)DIR=$withval; \ + AC_MSG_NOTICE([Setting m4_toupper($1)DIR to $withval]); \ GIT_CONF_APPEND_LINE(${PACKAGE}DIR=$withval); \ fi \ ])# GIT_PARSE_WITH @@ -86,9 +90,16 @@ AC_ARG_WITH([lib], [if test "$withval" = "no" || test "$withval" = "yes"; then \ AC_MSG_WARN([You should provide name for --with-lib=ARG]); \ else \ + lib=$withval; \ + AC_MSG_NOTICE([Setting lib to '$lib']); \ GIT_CONF_APPEND_LINE(lib=$withval); \ fi; \ ],[]) + +if test -z "$lib"; then + AC_MSG_NOTICE([Setting lib to 'lib' (the default)]) + lib=lib +fi # # Define SHELL_PATH to provide path to shell. GIT_ARG_SET_PATH(shell) From 08df6a3086f4bcd5b224d442f103a7f63a605967 Mon Sep 17 00:00:00 2001 From: Ben Walton Date: Thu, 12 Mar 2009 15:20:07 -0400 Subject: [PATCH 084/289] configure: reorganize flow of argument checks Move the argument tests from the 'site overrides' so that they are ahead of any library tests. This allows for library tests to take user specified paths into account. The intent here is to avoid things like NO_DEFLATE_BOUND being set due to finding old zlib when the user has specified an alternate location for zlib. (Ignore the fact that properly set *FLAGS can avoid solve this issue.) Signed-off-by: Ben Walton Signed-off-by: Junio C Hamano --- configure.ac | 174 +++++++++++++++++++++++++-------------------------- 1 file changed, 87 insertions(+), 87 deletions(-) diff --git a/configure.ac b/configure.ac index 0b314d7359..0bff4808c0 100644 --- a/configure.ac +++ b/configure.ac @@ -100,6 +100,93 @@ if test -z "$lib"; then AC_MSG_NOTICE([Setting lib to 'lib' (the default)]) lib=lib fi + +## Site configuration (override autodetection) +## --with-PACKAGE[=ARG] and --without-PACKAGE +AC_MSG_NOTICE([CHECKS for site configuration]) +# +# Define NO_SVN_TESTS if you want to skip time-consuming SVN interoperability +# tests. These tests take up a significant amount of the total test time +# but are not needed unless you plan to talk to SVN repos. +# +# Define MOZILLA_SHA1 environment variable when running make to make use of +# a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast +# on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default +# choice) has very fast version optimized for i586. +# +# Define PPC_SHA1 environment variable when running make to make use of +# a bundled SHA1 routine optimized for PowerPC. +# +# Define ARM_SHA1 environment variable when running make to make use of +# a bundled SHA1 routine optimized for ARM. +# +# Define NO_OPENSSL environment variable if you do not have OpenSSL. +# This also implies MOZILLA_SHA1. +# +# Define OPENSSLDIR=/foo/bar if your openssl header and library files are in +# /foo/bar/include and /foo/bar/lib directories. +AC_ARG_WITH(openssl, +AS_HELP_STRING([--with-openssl],[use OpenSSL library (default is YES)]) +AS_HELP_STRING([], [ARG can be prefix for openssl library and headers]),\ +GIT_PARSE_WITH(openssl)) +# +# Define NO_CURL if you do not have curl installed. git-http-pull and +# git-http-push are not built, and you cannot use http:// and https:// +# transports. +# +# Define CURLDIR=/foo/bar if your curl header and library files are in +# /foo/bar/include and /foo/bar/lib directories. +AC_ARG_WITH(curl, +AS_HELP_STRING([--with-curl],[support http(s):// transports (default is YES)]) +AS_HELP_STRING([], [ARG can be also prefix for curl library and headers]), +GIT_PARSE_WITH(curl)) +# +# Define NO_EXPAT if you do not have expat installed. git-http-push is +# not built, and you cannot push using http:// and https:// transports. +# +# Define EXPATDIR=/foo/bar if your expat header and library files are in +# /foo/bar/include and /foo/bar/lib directories. +AC_ARG_WITH(expat, +AS_HELP_STRING([--with-expat], +[support git-push using http:// and https:// transports via WebDAV (default is YES)]) +AS_HELP_STRING([], [ARG can be also prefix for expat library and headers]), +GIT_PARSE_WITH(expat)) +# +# Define NO_FINK if you are building on Darwin/Mac OS X, have Fink +# installed in /sw, but don't want GIT to link against any libraries +# installed there. If defined you may specify your own (or Fink's) +# include directories and library directories by defining CFLAGS +# and LDFLAGS appropriately. +# +# Define NO_DARWIN_PORTS if you are building on Darwin/Mac OS X, +# have DarwinPorts installed in /opt/local, but don't want GIT to +# link against any libraries installed there. If defined you may +# specify your own (or DarwinPort's) include directories and +# library directories by defining CFLAGS and LDFLAGS appropriately. +# +# Define NO_MMAP if you want to avoid mmap. +# +# Define NO_ICONV if your libc does not properly support iconv. +AC_ARG_WITH(iconv, +AS_HELP_STRING([--without-iconv], +[if your architecture doesn't properly support iconv]) +AS_HELP_STRING([--with-iconv=PATH], +[PATH is prefix for libiconv library and headers]) +AS_HELP_STRING([], +[used only if you need linking with libiconv]), +GIT_PARSE_WITH(iconv)) + +## --enable-FEATURE[=ARG] and --disable-FEATURE +# +# Define USE_NSEC below if you want git to care about sub-second file mtimes +# and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and +# it will BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely +# randomly break unless your underlying filesystem supports those sub-second +# times (my ext3 doesn't). +# +# Define USE_STDEV below if you want git to care about the underlying device +# change being considered an inode change from the update-index perspective. + # # Define SHELL_PATH to provide path to shell. GIT_ARG_SET_PATH(shell) @@ -526,93 +613,6 @@ AC_SUBST(PTHREAD_LIBS) AC_SUBST(NO_PTHREADS) AC_SUBST(THREADED_DELTA_SEARCH) -## Site configuration (override autodetection) -## --with-PACKAGE[=ARG] and --without-PACKAGE -AC_MSG_NOTICE([CHECKS for site configuration]) -# -# Define NO_SVN_TESTS if you want to skip time-consuming SVN interoperability -# tests. These tests take up a significant amount of the total test time -# but are not needed unless you plan to talk to SVN repos. -# -# Define MOZILLA_SHA1 environment variable when running make to make use of -# a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast -# on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default -# choice) has very fast version optimized for i586. -# -# Define PPC_SHA1 environment variable when running make to make use of -# a bundled SHA1 routine optimized for PowerPC. -# -# Define ARM_SHA1 environment variable when running make to make use of -# a bundled SHA1 routine optimized for ARM. -# -# Define NO_OPENSSL environment variable if you do not have OpenSSL. -# This also implies MOZILLA_SHA1. -# -# Define OPENSSLDIR=/foo/bar if your openssl header and library files are in -# /foo/bar/include and /foo/bar/lib directories. -AC_ARG_WITH(openssl, -AS_HELP_STRING([--with-openssl],[use OpenSSL library (default is YES)]) -AS_HELP_STRING([], [ARG can be prefix for openssl library and headers]),\ -GIT_PARSE_WITH(openssl)) -# -# Define NO_CURL if you do not have curl installed. git-http-pull and -# git-http-push are not built, and you cannot use http:// and https:// -# transports. -# -# Define CURLDIR=/foo/bar if your curl header and library files are in -# /foo/bar/include and /foo/bar/lib directories. -AC_ARG_WITH(curl, -AS_HELP_STRING([--with-curl],[support http(s):// transports (default is YES)]) -AS_HELP_STRING([], [ARG can be also prefix for curl library and headers]), -GIT_PARSE_WITH(curl)) -# -# Define NO_EXPAT if you do not have expat installed. git-http-push is -# not built, and you cannot push using http:// and https:// transports. -# -# Define EXPATDIR=/foo/bar if your expat header and library files are in -# /foo/bar/include and /foo/bar/lib directories. -AC_ARG_WITH(expat, -AS_HELP_STRING([--with-expat], -[support git-push using http:// and https:// transports via WebDAV (default is YES)]) -AS_HELP_STRING([], [ARG can be also prefix for expat library and headers]), -GIT_PARSE_WITH(expat)) -# -# Define NO_FINK if you are building on Darwin/Mac OS X, have Fink -# installed in /sw, but don't want GIT to link against any libraries -# installed there. If defined you may specify your own (or Fink's) -# include directories and library directories by defining CFLAGS -# and LDFLAGS appropriately. -# -# Define NO_DARWIN_PORTS if you are building on Darwin/Mac OS X, -# have DarwinPorts installed in /opt/local, but don't want GIT to -# link against any libraries installed there. If defined you may -# specify your own (or DarwinPort's) include directories and -# library directories by defining CFLAGS and LDFLAGS appropriately. -# -# Define NO_MMAP if you want to avoid mmap. -# -# Define NO_ICONV if your libc does not properly support iconv. -AC_ARG_WITH(iconv, -AS_HELP_STRING([--without-iconv], -[if your architecture doesn't properly support iconv]) -AS_HELP_STRING([--with-iconv=PATH], -[PATH is prefix for libiconv library and headers]) -AS_HELP_STRING([], -[used only if you need linking with libiconv]), -GIT_PARSE_WITH(iconv)) - -## --enable-FEATURE[=ARG] and --disable-FEATURE -# -# Define USE_NSEC below if you want git to care about sub-second file mtimes -# and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and -# it will BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely -# randomly break unless your underlying filesystem supports those sub-second -# times (my ext3 doesn't). -# -# Define USE_STDEV below if you want git to care about the underlying device -# change being considered an inode change from the update-index perspective. - - ## Output files AC_CONFIG_FILES(["${config_file}":"${config_in}":"${config_append}"]) AC_OUTPUT From 918c8120172514704809cbdf889794d8b1345d0a Mon Sep 17 00:00:00 2001 From: Ben Walton Date: Thu, 12 Mar 2009 15:20:08 -0400 Subject: [PATCH 085/289] configure: add macros to stash FLAG variables Allow for quick stash/unstash of CPPFLAGS and LDFLAGS. Library tests can now be easily bracketted with these macros to allow for values set in user/site arguments. Signed-off-by: Ben Walton Signed-off-by: Junio C Hamano --- configure.ac | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/configure.ac b/configure.ac index 0bff4808c0..469c9a98d2 100644 --- a/configure.ac +++ b/configure.ac @@ -80,6 +80,32 @@ AC_DEFUN([GIT_CHECK_FUNC],[AC_CHECK_FUNC([$1],[ AC_SEARCH_LIBS([$1],, [$2],[$3]) ],[$3])]) + +dnl +dnl GIT_STASH_FLAGS(BASEPATH_VAR) +dnl ----------------------------- +dnl Allow for easy stashing of LDFLAGS and CPPFLAGS before running +dnl tests that may want to take user settings into account. +AC_DEFUN([GIT_STASH_FLAGS],[ +if test -n "$1"; then + old_CPPFLAGS="$CPPFLAGS" + old_LDFLAGS="$LDFLAGS" + CPPFLAGS="-I$1/include $CPPFLAGS" + LDFLAGS="-L$1/$lib $LDFLAGS" +fi +]) + +dnl +dnl GIT_UNSTASH_FLAGS(BASEPATH_VAR) +dnl ----------------------------- +dnl Restore the stashed *FLAGS values. +AC_DEFUN([GIT_UNSTASH_FLAGS],[ +if test -n "$1"; then + CPPFLAGS="$old_CPPFLAGS" + LDFLAGS="$old_LDFLAGS" +fi +]) + ## Site configuration related to programs (before tests) ## --with-PACKAGE[=ARG] and --without-PACKAGE # From 310386f07b5ef3dc337db1be37345fbd8d7a3af8 Mon Sep 17 00:00:00 2001 From: Ben Walton Date: Thu, 12 Mar 2009 15:20:09 -0400 Subject: [PATCH 086/289] configure: wrap some library tests with GIT_STASH_FLAGS Libraries that can have user specificed base paths are wrapped with GIT_STASH_FLAGS/GIT_UNSTASH_FLAGS to ensure that the proper versions on the system are tested. This ensures, for example, that the zlib tests for deflateUnbound are done with the version of zlib requested by the user. This is most useful in the absence of good settings for CPPFLAGS and/or LDFLAGS. Signed-off-by: Ben Walton Signed-off-by: Junio C Hamano --- configure.ac | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 469c9a98d2..fe9d7eb463 100644 --- a/configure.ac +++ b/configure.ac @@ -315,33 +315,57 @@ AC_MSG_NOTICE([CHECKS for libraries]) # # Define NO_OPENSSL environment variable if you do not have OpenSSL. # Define NEEDS_SSL_WITH_CRYPTO if you need -lcrypto with -lssl (Darwin). + +GIT_STASH_FLAGS($OPENSSLDIR) + AC_CHECK_LIB([crypto], [SHA1_Init], [NEEDS_SSL_WITH_CRYPTO=], [AC_CHECK_LIB([ssl], [SHA1_Init], [NEEDS_SSL_WITH_CRYPTO=YesPlease NEEDS_SSL_WITH_CRYPTO=], [NO_OPENSSL=YesPlease])]) + +GIT_UNSTASH_FLAGS($OPENSSLDIR) + AC_SUBST(NEEDS_SSL_WITH_CRYPTO) AC_SUBST(NO_OPENSSL) + # # Define NO_CURL if you do not have libcurl installed. git-http-pull and # git-http-push are not built, and you cannot use http:// and https:// # transports. + +GIT_STASH_FLAGS($CURLDIR) + AC_CHECK_LIB([curl], [curl_global_init], [NO_CURL=], [NO_CURL=YesPlease]) + +GIT_UNSTASH_FLAGS($CURLDIR) + AC_SUBST(NO_CURL) + # # Define NO_EXPAT if you do not have expat installed. git-http-push is # not built, and you cannot push using http:// and https:// transports. + +GIT_STASH_FLAGS($EXPATDIR) + AC_CHECK_LIB([expat], [XML_ParserCreate], [NO_EXPAT=], [NO_EXPAT=YesPlease]) + +GIT_UNSTASH_FLAGS($EXPATDIR) + AC_SUBST(NO_EXPAT) + # # Define NEEDS_LIBICONV if linking with libc is not enough (Darwin and # some Solaris installations). # Define NO_ICONV if neither libc nor libiconv support iconv. + +GIT_STASH_FLAGS($ICONVDIR) + AC_DEFUN([ICONVTEST_SRC], [ #include @@ -365,11 +389,17 @@ AC_LINK_IFELSE(ICONVTEST_SRC, [AC_MSG_RESULT([no]) NO_ICONV=YesPlease]) LIBS="$old_LIBS"]) + +GIT_UNSTASH_FLAGS($ICONVDIR) + AC_SUBST(NEEDS_LIBICONV) AC_SUBST(NO_ICONV) -test -n "$NEEDS_LIBICONV" && LIBS="$LIBS -liconv" + # # Define NO_DEFLATE_BOUND if deflateBound is missing from zlib. + +GIT_STASH_FLAGS($ZLIB_PATH) + AC_DEFUN([ZLIBTEST_SRC], [ #include @@ -387,7 +417,11 @@ AC_LINK_IFELSE(ZLIBTEST_SRC, [AC_MSG_RESULT([no]) NO_DEFLATE_BOUND=yes]) LIBS="$old_LIBS" + +GIT_UNSTASH_FLAGS($ZLIB_PATH) + AC_SUBST(NO_DEFLATE_BOUND) + # # Define NEEDS_SOCKET if linking with libc is not enough (SunOS, # Patrick Mauritz). From 29adc8baf997e143d8aeac9a0aeb257539d32e6d Mon Sep 17 00:00:00 2001 From: Ben Walton Date: Thu, 12 Mar 2009 15:20:10 -0400 Subject: [PATCH 087/289] configure: asciidoc version test cleanup Redirect stderr to /dev/null instead of stdout. This discards warnings generated by python 2.6 related to the reorganization of functions within modules. The warnings were causing the version detection to break. Signed-off-by: Ben Walton Signed-off-by: Junio C Hamano --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index fe9d7eb463..f4b8e49dc3 100644 --- a/configure.ac +++ b/configure.ac @@ -291,7 +291,7 @@ fi AC_CHECK_PROGS(ASCIIDOC, [asciidoc]) if test -n "$ASCIIDOC"; then AC_MSG_CHECKING([for asciidoc version]) - asciidoc_version=`$ASCIIDOC --version 2>&1` + asciidoc_version=`$ASCIIDOC --version 2>/dev/null` case "${asciidoc_version}" in asciidoc' '8*) ASCIIDOC8=YesPlease From a8304f7a70832a8e333fcf547589bc9d3bc0ca3c Mon Sep 17 00:00:00 2001 From: Ben Walton Date: Thu, 12 Mar 2009 15:20:11 -0400 Subject: [PATCH 088/289] configure: make iconv tests aware of user arguments --with-iconv is now taken into account when doing the tests for iconv. If the user requests alternate handling for libiconv, the -liconv test is run before the -lc test. Signed-off-by: Ben Walton Signed-off-by: Junio C Hamano --- configure.ac | 48 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/configure.ac b/configure.ac index f4b8e49dc3..6fe4bfe738 100644 --- a/configure.ac +++ b/configure.ac @@ -375,20 +375,35 @@ int main(void) return 0; } ]) -AC_MSG_CHECKING([for iconv in -lc]) -AC_LINK_IFELSE(ICONVTEST_SRC, + +if test -n "$ICONVDIR"; then + lib_order="-liconv -lc" +else + lib_order="-lc -liconv" +fi + +NO_ICONV=YesPlease + +for l in $lib_order; do + if test "$l" = "-liconv"; then + NEEDS_LIBICONV=YesPlease + else + NEEDS_LIBICONV= + fi + + old_LIBS="$LIBS" + LIBS="$LIBS $l" + AC_MSG_CHECKING([for iconv in $l]) + AC_LINK_IFELSE(ICONVTEST_SRC, [AC_MSG_RESULT([yes]) - NEEDS_LIBICONV=], - [AC_MSG_RESULT([no]) - old_LIBS="$LIBS" - LIBS="$LIBS -liconv" - AC_MSG_CHECKING([for iconv in -liconv]) - AC_LINK_IFELSE(ICONVTEST_SRC, - [AC_MSG_RESULT([yes]) - NEEDS_LIBICONV=YesPlease], - [AC_MSG_RESULT([no]) - NO_ICONV=YesPlease]) - LIBS="$old_LIBS"]) + NO_ICONV= + break], + [AC_MSG_RESULT([no])]) + LIBS="$old_LIBS" +done + +#in case of break +LIBS="$old_LIBS" GIT_UNSTASH_FLAGS($ICONVDIR) @@ -455,13 +470,18 @@ int main(void) return 0; } ]]) + +GIT_STASH_FLAGS($ICONVDIR) + AC_MSG_CHECKING([for old iconv()]) AC_COMPILE_IFELSE(OLDICONVTEST_SRC, [AC_MSG_RESULT([no])], [AC_MSG_RESULT([yes]) OLD_ICONV=UnfortunatelyYes]) -AC_SUBST(OLD_ICONV) +GIT_UNSTASH_FLAGS($ICONVDIR) + +AC_SUBST(OLD_ICONV) ## Checks for typedefs, structures, and compiler characteristics. AC_MSG_NOTICE([CHECKS for typedefs, structures, and compiler characteristics]) From 1973b0d790155b5d8a32a6f7401116fad2205721 Mon Sep 17 00:00:00 2001 From: Ben Walton Date: Thu, 12 Mar 2009 15:20:12 -0400 Subject: [PATCH 089/289] configure: rework pthread handling to allow for user defined flags The tests for POSIX threads can now be controlled by the user with the --enable-pthreads=FLAGS option. If this is set (to some value other than yes or no), the value is passed to the compiler. Thread support is based solely on the outcome of this test. The user may specify not to use threading at all or to use the default tests (first -pthread then -lpthread) by not specifying FLAGS when passing --enable-pthreads. Signed-off-by: Ben Walton Signed-off-by: Junio C Hamano --- configure.ac | 89 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 74 insertions(+), 15 deletions(-) diff --git a/configure.ac b/configure.ac index 6fe4bfe738..4e728bca35 100644 --- a/configure.ac +++ b/configure.ac @@ -127,6 +127,27 @@ if test -z "$lib"; then lib=lib fi +AC_ARG_ENABLE([pthreads], + [AS_HELP_STRING([--enable-pthreads=FLAGS], + [FLAGS is the value to pass to the compiler to enable POSIX Threads.] + [The default if FLAGS is not specified is to try first -pthread] + [and then -lpthread.] + [--without-pthreads will disable threading.])], +[ +if test "x$enableval" = "xyes"; then + AC_MSG_NOTICE([Will try -pthread then -lpthread to enable POSIX Threads]) +elif test "x$enableval" != "xno"; then + PTHREAD_CFLAGS=$enableval + AC_MSG_NOTICE([Setting '$PTHREAD_CFLAGS' as the FLAGS to enable POSIX Threads]) +else + AC_MSG_NOTICE([POSIX Threads will be disabled.]) + NO_PTHREADS=YesPlease + USER_NOPTHREAD=1 +fi], +[ + AC_MSG_NOTICE([Will try -pthread then -lpthread to enable POSIX Threads.]) +]) + ## Site configuration (override autodetection) ## --with-PACKAGE[=ARG] and --without-PACKAGE AC_MSG_NOTICE([CHECKS for site configuration]) @@ -672,23 +693,61 @@ AC_SUBST(NO_MKDTEMP) # # Define PTHREAD_LIBS to the linker flag used for Pthread support and define # THREADED_DELTA_SEARCH if Pthreads are available. -AC_LANG_CONFTEST([AC_LANG_PROGRAM( - [[#include ]], - [[pthread_mutex_t test_mutex;]] -)]) -${CC} -pthread conftest.c -o conftest.o > /dev/null 2>&1 -if test $? -eq 0;then - PTHREAD_LIBS="-pthread" - THREADED_DELTA_SEARCH=YesPlease +AC_DEFUN([PTHREADTEST_SRC], [ +#include + +int main(void) +{ + pthread_mutex_t test_mutex; + return (0); +} +]) + +dnl AC_LANG_CONFTEST([AC_LANG_PROGRAM( +dnl [[#include ]], +dnl [[pthread_mutex_t test_mutex;]] +dnl )]) + +NO_PTHREADS=UnfortunatelyYes +THREADED_DELTA_SEARCH= +PTHREAD_LIBS= + +if test -n "$USER_NOPTHREAD"; then + AC_MSG_NOTICE([Skipping POSIX Threads at user request.]) +# handle these separately since PTHREAD_CFLAGS could be '-lpthreads +# -D_REENTRANT' or some such. +elif test -z "$PTHREAD_CFLAGS"; then + for opt in -pthread -lpthread; do + old_CFLAGS="$CFLAGS" + CFLAGS="$opt $CFLAGS" + AC_MSG_CHECKING([Checking for POSIX Threads with '$opt']) + AC_LINK_IFELSE(PTHREADTEST_SRC, + [AC_MSG_RESULT([yes]) + NO_PTHREADS= + PTHREAD_LIBS="$opt" + THREADED_DELTA_SEARCH=YesPlease + break + ], + [AC_MSG_RESULT([no])]) + CFLAGS="$old_CFLAGS" + done else - ${CC} -lpthread conftest.c -o conftest.o > /dev/null 2>&1 - if test $? -eq 0;then - PTHREAD_LIBS="-lpthread" - THREADED_DELTA_SEARCH=YesPlease - else - NO_PTHREADS=UnfortunatelyYes - fi + old_CFLAGS="$CFLAGS" + CFLAGS="$PTHREAD_CFLAGS $CFLAGS" + AC_MSG_CHECKING([Checking for POSIX Threads with '$PTHREAD_CFLAGS']) + AC_LINK_IFELSE(PTHREADTEST_SRC, + [AC_MSG_RESULT([yes]) + NO_PTHREADS= + PTHREAD_LIBS="$PTHREAD_CFLAGS" + THREADED_DELTA_SEARCH=YesPlease + ], + [AC_MSG_RESULT([no])]) + + CFLAGS="$old_CFLAGS" fi + +CFLAGS="$old_CFLAGS" + AC_SUBST(PTHREAD_LIBS) AC_SUBST(NO_PTHREADS) AC_SUBST(THREADED_DELTA_SEARCH) From 06f33c1735bf76e02f3e2601cde5161e969872a7 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 13 Mar 2009 21:24:08 -0700 Subject: [PATCH 090/289] Read attributes from the index that is being checked out Traditionally we used .gitattributes file from the work tree if exists, and otherwise read from the index as a fallback. When switching to a branch that has an updated .gitattributes file, and entries in it give different attributes to other paths being checked out, we should instead read from the .gitattributes in the index. This breaks a use case of fixing incorrect entries in the .gitattributes in the work tree (without adding it to the index) and checking other paths out, though. $ edit .gitattributes ;# mark foo.dat as binary $ rm foo.dat $ git checkout foo.dat Signed-off-by: Junio C Hamano --- attr.c | 73 +++++++++++++++++++++++++++++++++++++------------- attr.h | 6 +++++ unpack-trees.c | 3 +++ 3 files changed, 64 insertions(+), 18 deletions(-) diff --git a/attr.c b/attr.c index 17f6a4dca5..43259e5b01 100644 --- a/attr.c +++ b/attr.c @@ -1,3 +1,4 @@ +#define NO_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "attr.h" @@ -318,6 +319,9 @@ static struct attr_stack *read_attr_from_array(const char **list) return res; } +static enum git_attr_direction direction; +static struct index_state *use_index; + static struct attr_stack *read_attr_from_file(const char *path, int macro_ok) { FILE *fp = fopen(path, "r"); @@ -340,9 +344,10 @@ static void *read_index_data(const char *path) unsigned long sz; enum object_type type; void *data; + struct index_state *istate = use_index ? use_index : &the_index; len = strlen(path); - pos = cache_name_pos(path, len); + pos = index_name_pos(istate, path, len); if (pos < 0) { /* * We might be in the middle of a merge, in which @@ -350,15 +355,15 @@ static void *read_index_data(const char *path) */ int i; for (i = -pos - 1; - (pos < 0 && i < active_nr && - !strcmp(active_cache[i]->name, path)); + (pos < 0 && i < istate->cache_nr && + !strcmp(istate->cache[i]->name, path)); i++) - if (ce_stage(active_cache[i]) == 2) + if (ce_stage(istate->cache[i]) == 2) pos = i; } if (pos < 0) return NULL; - data = read_sha1_file(active_cache[pos]->sha1, &type, &sz); + data = read_sha1_file(istate->cache[pos]->sha1, &type, &sz); if (!data || type != OBJ_BLOB) { free(data); return NULL; @@ -366,27 +371,17 @@ static void *read_index_data(const char *path) return data; } -static struct attr_stack *read_attr(const char *path, int macro_ok) +static struct attr_stack *read_attr_from_index(const char *path, int macro_ok) { struct attr_stack *res; char *buf, *sp; int lineno = 0; - res = read_attr_from_file(path, macro_ok); - if (res) - return res; - - res = xcalloc(1, sizeof(*res)); - - /* - * There is no checked out .gitattributes file there, but - * we might have it in the index. We allow operation in a - * sparsely checked out work tree, so read from it. - */ buf = read_index_data(path); if (!buf) - return res; + return NULL; + res = xcalloc(1, sizeof(*res)); for (sp = buf; *sp; ) { char *ep; int more; @@ -401,6 +396,30 @@ static struct attr_stack *read_attr(const char *path, int macro_ok) return res; } +static struct attr_stack *read_attr(const char *path, int macro_ok) +{ + struct attr_stack *res; + + if (direction == GIT_ATTR_CHECKOUT) { + res = read_attr_from_index(path, macro_ok); + if (!res) + res = read_attr_from_file(path, macro_ok); + } + else { + res = read_attr_from_file(path, macro_ok); + if (!res) + /* + * There is no checked out .gitattributes file there, but + * we might have it in the index. We allow operation in a + * sparsely checked out work tree, so read from it. + */ + res = read_attr_from_index(path, macro_ok); + } + if (!res) + res = xcalloc(1, sizeof(*res)); + return res; +} + #if DEBUG_ATTR static void debug_info(const char *what, struct attr_stack *elem) { @@ -428,6 +447,15 @@ static void debug_set(const char *what, const char *match, struct git_attr *attr #define debug_set(a,b,c,d) do { ; } while (0) #endif +static void drop_attr_stack(void) +{ + while (attr_stack) { + struct attr_stack *elem = attr_stack; + attr_stack = elem->prev; + free_attr_elem(elem); + } +} + static void bootstrap_attr_stack(void) { if (!attr_stack) { @@ -642,3 +670,12 @@ int git_checkattr(const char *path, int num, struct git_attr_check *check) return 0; } + +void git_attr_set_direction(enum git_attr_direction new, struct index_state *istate) +{ + enum git_attr_direction old = direction; + direction = new; + if (new != old) + drop_attr_stack(); + use_index = istate; +} diff --git a/attr.h b/attr.h index f1c2038b09..3a2f4ec1a0 100644 --- a/attr.h +++ b/attr.h @@ -31,4 +31,10 @@ struct git_attr_check { int git_checkattr(const char *path, int, struct git_attr_check *); +enum git_attr_direction { + GIT_ATTR_CHECKIN, + GIT_ATTR_CHECKOUT +}; +void git_attr_set_direction(enum git_attr_direction, struct index_state *); + #endif /* ATTR_H */ diff --git a/unpack-trees.c b/unpack-trees.c index e547282ed5..661218c5ae 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -7,6 +7,7 @@ #include "unpack-trees.h" #include "progress.h" #include "refs.h" +#include "attr.h" /* * Error messages expected by scripts out of plumbing commands such as @@ -105,6 +106,7 @@ static int check_updates(struct unpack_trees_options *o) cnt = 0; } + git_attr_set_direction(GIT_ATTR_CHECKOUT, &o->result); for (i = 0; i < index->cache_nr; i++) { struct cache_entry *ce = index->cache[i]; @@ -130,6 +132,7 @@ static int check_updates(struct unpack_trees_options *o) } } stop_progress(&progress); + git_attr_set_direction(GIT_ATTR_CHECKIN, NULL); return errs != 0; } From 5bcf109cdf7f7b676600883be8dc7dbf26ddb055 Mon Sep 17 00:00:00 2001 From: Kjetil Barvik Date: Sun, 15 Mar 2009 12:38:55 +0100 Subject: [PATCH 091/289] checkout bugfix: use stat.mtime instead of stat.ctime in two places Commit e1afca4fd "write_index(): update index_state->timestamp after flushing to disk" on 2009-02-23 used stat.ctime to record the timestamp of the index-file. This is wrong, so fix this and use the correct stat.mtime timestamp instead. Commit 110c46a909 "Not all systems use st_[cm]tim field for ns resolution file timestamp" on 2009-03-08, has a similar bug for the builtin-fetch-pack.c file. Signed-off-by: Kjetil Barvik Signed-off-by: Junio C Hamano --- builtin-fetch-pack.c | 2 +- read-cache.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c index 1d7e02326f..8b33861681 100644 --- a/builtin-fetch-pack.c +++ b/builtin-fetch-pack.c @@ -807,7 +807,7 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args, die("shallow file was removed during fetch"); } else if (st.st_mtime != mtime.sec #ifdef USE_NSEC - || ST_CTIME_NSEC(st) != mtime.nsec + || ST_MTIME_NSEC(st) != mtime.nsec #endif ) die("shallow file was changed during fetch"); diff --git a/read-cache.c b/read-cache.c index 7f74c8d161..3f587110cb 100644 --- a/read-cache.c +++ b/read-cache.c @@ -1563,8 +1563,8 @@ int write_index(struct index_state *istate, int newfd) if (ce_flush(&c, newfd) || fstat(newfd, &st)) return -1; - istate->timestamp.sec = (unsigned int)st.st_ctime; - istate->timestamp.nsec = ST_CTIME_NSEC(st); + istate->timestamp.sec = (unsigned int)st.st_mtime; + istate->timestamp.nsec = ST_MTIME_NSEC(st); return 0; } From 9326d49412c8c154b43cb7eba2a8692e9703b0f4 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 16 Mar 2009 00:35:09 -0700 Subject: [PATCH 092/289] Remove total confusion from git-fetch and git-push The config file is not the only place remotes are defined, and without consulting .git/remotes and .git/branches, you won't know if "origin" is configured by the user. Don't give up too early and insult the user with a wisecrack "Where do you want to fetch from today?" The only thing the previous patch seems to want to prevent from happening is a lazy "git fetch/push" that does not say where-from/to to produce an error message 'origin not found', and we can do that by not letting add_url_alias() to turn a nickname "origin" literally into a pathname "origin" without changing the rest of the logic. Signed-off-by: Junio C Hamano --- remote.c | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/remote.c b/remote.c index 199830ea93..c7a8c2b1fb 100644 --- a/remote.c +++ b/remote.c @@ -105,16 +105,6 @@ static void add_url_alias(struct remote *remote, const char *url) add_url(remote, alias_url(url)); } -static struct remote *get_remote_by_name(const char *name) -{ - int i; - for (i = 0; i < remotes_nr; i++) { - if (!strcmp(name, remotes[i]->name)) - return remotes[i]; - } - return NULL; -} - static struct remote *make_remote(const char *name, int len) { struct remote *ret; @@ -665,20 +655,15 @@ struct remote *remote_get(const char *name) name = default_remote_name; name_given = explicit_default_remote_name; } - if (name_given) - ret = make_remote(name, 0); - else { - ret = get_remote_by_name(name); - if (!ret) - return NULL; - } + + ret = make_remote(name, 0); if (valid_remote_nick(name)) { if (!ret->url) read_remotes_file(ret); if (!ret->url) read_branches_file(ret); } - if (!ret->url) + if (name_given && !ret->url) add_url_alias(ret, name); if (!ret->url) return NULL; From 7ee3760e2c8f181f22d4b80afc03309e4f764385 Mon Sep 17 00:00:00 2001 From: Michael J Gruber Date: Mon, 16 Mar 2009 18:03:11 +0100 Subject: [PATCH 093/289] test-lib.sh: Test for presence of git-init in the right path. It just happens so that when GIT_EXEC_PATH points to a compiled checkout of git.git it contains "git". Since this is not true in general make test-lib check for "git-init" which is always in GIT_EXEC_PATH. Signed-off-by: Michael J Gruber Signed-off-by: Junio C Hamano --- t/test-lib.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/test-lib.sh b/t/test-lib.sh index 1e01a912ab..dace24c541 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -454,7 +454,7 @@ test_create_repo () { repo="$1" mkdir -p "$repo" cd "$repo" || error "Cannot setup test environment" - "$GIT_EXEC_PATH/git" init "--template=$owd/../templates/blt/" >&3 2>&4 || + "$GIT_EXEC_PATH/git-init" "--template=$owd/../templates/blt/" >&3 2>&4 || error "cannot run git init -- have you built things yet?" mv .git/hooks .git/hooks-disabled cd "$owd" From 6720721e152aee230387546efac865319f025597 Mon Sep 17 00:00:00 2001 From: Michael J Gruber Date: Mon, 16 Mar 2009 18:03:12 +0100 Subject: [PATCH 094/289] test-lib.sh: Allow running the test suite against installed git Introduce variables GIT_TEST_INSTALLED and GIT_TEST_EXEC_PATH such that the test suite can be run against a git which is installed at GIT_TEST_INSTALLED with subcommands at GIT_TEST_EXEC_PATH. GIT_TEST_INSTALLED defaults to the git.git checkout, GIT_TEST_EXEC_PATH defaults to the output of '$GIT_TEST_INSTALLED/git --exec-path'. Run the suite e.g. as GIT_TEST_INSTALLED=/some/path make test but note that this requires and uses parts of a compiled git in the git.git checkout: test helpers, templates and perl libraries are taken from there. Signed-off-by: Michael J Gruber Signed-off-by: Junio C Hamano --- t/test-lib.sh | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/t/test-lib.sh b/t/test-lib.sh index dace24c541..a70b633dd9 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -514,8 +514,16 @@ test_done () { TEST_DIRECTORY=$(pwd) if test -z "$valgrind" then - PATH=$TEST_DIRECTORY/..:$PATH - GIT_EXEC_PATH=$TEST_DIRECTORY/.. + if test -z "$GIT_TEST_INSTALLED" + then + PATH=$TEST_DIRECTORY/..:$PATH + GIT_EXEC_PATH=$TEST_DIRECTORY/.. + else + GIT_EXEC_PATH=$($GIT_TEST_INSTALLED/git --exec-path) || + error "Cannot run git from $GIT_TEST_INSTALLED." + PATH=$GIT_TEST_INSTALLED:$TEST_DIRECTORY/..:$PATH + GIT_EXEC_PATH=${GIT_TEST_EXEC_PATH:-$GIT_EXEC_PATH} + fi else make_symlink () { test -h "$2" && From 3f7cdf32996a9213a3085b182960f5ce2c0a6568 Mon Sep 17 00:00:00 2001 From: "David J. Mellor" Date: Mon, 16 Mar 2009 23:16:14 -0700 Subject: [PATCH 095/289] Documentation: minor grammatical fixes in git-archive.txt. Signed-off-by: David J. Mellor Signed-off-by: Junio C Hamano --- Documentation/git-archive.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/git-archive.txt b/Documentation/git-archive.txt index 5b3eb12c8a..ad38f7f39d 100644 --- a/Documentation/git-archive.txt +++ b/Documentation/git-archive.txt @@ -22,7 +22,7 @@ prepended to the filenames in the archive. 'git-archive' behaves differently when given a tree ID versus when given a commit ID or tag ID. In the first case the current time is -used as modification time of each file in the archive. In the latter +used as the modification time of each file in the archive. In the latter case the commit time as recorded in the referenced commit object is used instead. Additionally the commit ID is stored in a global extended pax header if the tar format is used; it can be extracted @@ -48,11 +48,11 @@ OPTIONS Prepend / to each filename in the archive. :: - This can be any options that the archiver backend understand. + This can be any options that the archiver backend understands. See next section. --remote=:: - Instead of making a tar archive from local repository, + Instead of making a tar archive from the local repository, retrieve a tar archive from a remote repository. --exec=:: @@ -105,7 +105,7 @@ EXAMPLES git archive --format=tar --prefix=junk/ HEAD | (cd /var/tmp/ && tar xf -):: Create a tar archive that contains the contents of the - latest commit on the current branch, and extracts it in + latest commit on the current branch, and extract it in the `/var/tmp/junk` directory. git archive --format=tar --prefix=git-1.4.0/ v1.4.0 | gzip >git-1.4.0.tar.gz:: From b3debd2b0c091990c420f78e074c0c160498e51d Mon Sep 17 00:00:00 2001 From: Petr Kodl Date: Sat, 24 Jan 2009 15:04:39 +0100 Subject: [PATCH 096/289] MinGW: a helper function that translates Win32 API error codes This function translates many possible Win32 error codes to suitable errno numbers. We will use it in our wrapper functions that need to call into Win32. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- compat/mingw.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/compat/mingw.c b/compat/mingw.c index 27bcf3fd6b..f66ad56aae 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -4,6 +4,119 @@ unsigned int _CRT_fmode = _O_BINARY; +static int err_win_to_posix(DWORD winerr) +{ + int error = ENOSYS; + switch(winerr) { + case ERROR_ACCESS_DENIED: error = EACCES; break; + case ERROR_ACCOUNT_DISABLED: error = EACCES; break; + case ERROR_ACCOUNT_RESTRICTION: error = EACCES; break; + case ERROR_ALREADY_ASSIGNED: error = EBUSY; break; + case ERROR_ALREADY_EXISTS: error = EEXIST; break; + case ERROR_ARITHMETIC_OVERFLOW: error = ERANGE; break; + case ERROR_BAD_COMMAND: error = EIO; break; + case ERROR_BAD_DEVICE: error = ENODEV; break; + case ERROR_BAD_DRIVER_LEVEL: error = ENXIO; break; + case ERROR_BAD_EXE_FORMAT: error = ENOEXEC; break; + case ERROR_BAD_FORMAT: error = ENOEXEC; break; + case ERROR_BAD_LENGTH: error = EINVAL; break; + case ERROR_BAD_PATHNAME: error = ENOENT; break; + case ERROR_BAD_PIPE: error = EPIPE; break; + case ERROR_BAD_UNIT: error = ENODEV; break; + case ERROR_BAD_USERNAME: error = EINVAL; break; + case ERROR_BROKEN_PIPE: error = EPIPE; break; + case ERROR_BUFFER_OVERFLOW: error = ENAMETOOLONG; break; + case ERROR_BUSY: error = EBUSY; break; + case ERROR_BUSY_DRIVE: error = EBUSY; break; + case ERROR_CALL_NOT_IMPLEMENTED: error = ENOSYS; break; + case ERROR_CANNOT_MAKE: error = EACCES; break; + case ERROR_CANTOPEN: error = EIO; break; + case ERROR_CANTREAD: error = EIO; break; + case ERROR_CANTWRITE: error = EIO; break; + case ERROR_CRC: error = EIO; break; + case ERROR_CURRENT_DIRECTORY: error = EACCES; break; + case ERROR_DEVICE_IN_USE: error = EBUSY; break; + case ERROR_DEV_NOT_EXIST: error = ENODEV; break; + case ERROR_DIRECTORY: error = EINVAL; break; + case ERROR_DIR_NOT_EMPTY: error = ENOTEMPTY; break; + case ERROR_DISK_CHANGE: error = EIO; break; + case ERROR_DISK_FULL: error = ENOSPC; break; + case ERROR_DRIVE_LOCKED: error = EBUSY; break; + case ERROR_ENVVAR_NOT_FOUND: error = EINVAL; break; + case ERROR_EXE_MARKED_INVALID: error = ENOEXEC; break; + case ERROR_FILENAME_EXCED_RANGE: error = ENAMETOOLONG; break; + case ERROR_FILE_EXISTS: error = EEXIST; break; + case ERROR_FILE_INVALID: error = ENODEV; break; + case ERROR_FILE_NOT_FOUND: error = ENOENT; break; + case ERROR_GEN_FAILURE: error = EIO; break; + case ERROR_HANDLE_DISK_FULL: error = ENOSPC; break; + case ERROR_INSUFFICIENT_BUFFER: error = ENOMEM; break; + case ERROR_INVALID_ACCESS: error = EACCES; break; + case ERROR_INVALID_ADDRESS: error = EFAULT; break; + case ERROR_INVALID_BLOCK: error = EFAULT; break; + case ERROR_INVALID_DATA: error = EINVAL; break; + case ERROR_INVALID_DRIVE: error = ENODEV; break; + case ERROR_INVALID_EXE_SIGNATURE: error = ENOEXEC; break; + case ERROR_INVALID_FLAGS: error = EINVAL; break; + case ERROR_INVALID_FUNCTION: error = ENOSYS; break; + case ERROR_INVALID_HANDLE: error = EBADF; break; + case ERROR_INVALID_LOGON_HOURS: error = EACCES; break; + case ERROR_INVALID_NAME: error = EINVAL; break; + case ERROR_INVALID_OWNER: error = EINVAL; break; + case ERROR_INVALID_PARAMETER: error = EINVAL; break; + case ERROR_INVALID_PASSWORD: error = EPERM; break; + case ERROR_INVALID_PRIMARY_GROUP: error = EINVAL; break; + case ERROR_INVALID_SIGNAL_NUMBER: error = EINVAL; break; + case ERROR_INVALID_TARGET_HANDLE: error = EIO; break; + case ERROR_INVALID_WORKSTATION: error = EACCES; break; + case ERROR_IO_DEVICE: error = EIO; break; + case ERROR_IO_INCOMPLETE: error = EINTR; break; + case ERROR_LOCKED: error = EBUSY; break; + case ERROR_LOCK_VIOLATION: error = EACCES; break; + case ERROR_LOGON_FAILURE: error = EACCES; break; + case ERROR_MAPPED_ALIGNMENT: error = EINVAL; break; + case ERROR_META_EXPANSION_TOO_LONG: error = E2BIG; break; + case ERROR_MORE_DATA: error = EPIPE; break; + case ERROR_NEGATIVE_SEEK: error = ESPIPE; break; + case ERROR_NOACCESS: error = EFAULT; break; + case ERROR_NONE_MAPPED: error = EINVAL; break; + case ERROR_NOT_ENOUGH_MEMORY: error = ENOMEM; break; + case ERROR_NOT_READY: error = EAGAIN; break; + case ERROR_NOT_SAME_DEVICE: error = EXDEV; break; + case ERROR_NO_DATA: error = EPIPE; break; + case ERROR_NO_MORE_SEARCH_HANDLES: error = EIO; break; + case ERROR_NO_PROC_SLOTS: error = EAGAIN; break; + case ERROR_NO_SUCH_PRIVILEGE: error = EACCES; break; + case ERROR_OPEN_FAILED: error = EIO; break; + case ERROR_OPEN_FILES: error = EBUSY; break; + case ERROR_OPERATION_ABORTED: error = EINTR; break; + case ERROR_OUTOFMEMORY: error = ENOMEM; break; + case ERROR_PASSWORD_EXPIRED: error = EACCES; break; + case ERROR_PATH_BUSY: error = EBUSY; break; + case ERROR_PATH_NOT_FOUND: error = ENOENT; break; + case ERROR_PIPE_BUSY: error = EBUSY; break; + case ERROR_PIPE_CONNECTED: error = EPIPE; break; + case ERROR_PIPE_LISTENING: error = EPIPE; break; + case ERROR_PIPE_NOT_CONNECTED: error = EPIPE; break; + case ERROR_PRIVILEGE_NOT_HELD: error = EACCES; break; + case ERROR_READ_FAULT: error = EIO; break; + case ERROR_SEEK: error = EIO; break; + case ERROR_SEEK_ON_DEVICE: error = ESPIPE; break; + case ERROR_SHARING_BUFFER_EXCEEDED: error = ENFILE; break; + case ERROR_SHARING_VIOLATION: error = EACCES; break; + case ERROR_STACK_OVERFLOW: error = ENOMEM; break; + case ERROR_SWAPERROR: error = ENOENT; break; + case ERROR_TOO_MANY_MODULES: error = EMFILE; break; + case ERROR_TOO_MANY_OPEN_FILES: error = EMFILE; break; + case ERROR_UNRECOGNIZED_MEDIA: error = ENXIO; break; + case ERROR_UNRECOGNIZED_VOLUME: error = ENODEV; break; + case ERROR_WAIT_NO_CHILDREN: error = ECHILD; break; + case ERROR_WRITE_FAULT: error = EIO; break; + case ERROR_WRITE_PROTECT: error = EROFS; break; + } + return error; +} + #undef open int mingw_open (const char *filename, int oflags, ...) { From 7be401e069758cc36d335241d9b80f9aeebf58c7 Mon Sep 17 00:00:00 2001 From: Petr Kodl Date: Sat, 24 Jan 2009 15:04:39 +0100 Subject: [PATCH 097/289] MinGW: a hardlink implementation Signed-off-by: Johannes Schindelin Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- compat/mingw.c | 21 +++++++++++++++++++++ compat/mingw.h | 3 +-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/compat/mingw.c b/compat/mingw.c index f66ad56aae..171fa85e4a 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -1118,3 +1118,24 @@ void mingw_open_html(const char *unixpath) printf("Launching default browser to display HTML ...\n"); ShellExecute(NULL, "open", htmlpath, NULL, "\\", 0); } + +int link(const char *oldpath, const char *newpath) +{ + typedef BOOL WINAPI (*T)(const char*, const char*, LPSECURITY_ATTRIBUTES); + static T create_hard_link = NULL; + if (!create_hard_link) { + create_hard_link = (T) GetProcAddress( + GetModuleHandle("kernel32.dll"), "CreateHardLinkA"); + if (!create_hard_link) + create_hard_link = (T)-1; + } + if (create_hard_link == (T)-1) { + errno = ENOSYS; + return -1; + } + if (!create_hard_link(newpath, oldpath, NULL)) { + errno = err_win_to_posix(GetLastError()); + return -1; + } + return 0; +} diff --git a/compat/mingw.h b/compat/mingw.h index 6e24686442..7e52f3607f 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -67,8 +67,6 @@ static inline int readlink(const char *path, char *buf, size_t bufsiz) { errno = ENOSYS; return -1; } static inline int symlink(const char *oldpath, const char *newpath) { errno = ENOSYS; return -1; } -static inline int link(const char *oldpath, const char *newpath) -{ errno = ENOSYS; return -1; } static inline int fchmod(int fildes, mode_t mode) { errno = ENOSYS; return -1; } static inline int fork(void) @@ -134,6 +132,7 @@ int getpagesize(void); /* defined in MinGW's libgcc.a */ struct passwd *getpwuid(int uid); int setitimer(int type, struct itimerval *in, struct itimerval *out); int sigaction(int sig, struct sigaction *in, struct sigaction *out); +int link(const char *oldpath, const char *newpath); /* * replacements of existing functions From bf71b4b3ee07291e97c4dabfb97e7397eec904e0 Mon Sep 17 00:00:00 2001 From: Carlos Rica Date: Tue, 17 Mar 2009 10:46:37 +0100 Subject: [PATCH 098/289] config: test for --replace-all with one argument and fix documentation. Option --replace-all only allows at least two arguments, so documentation was needing to be updated accordingly. A test showing that the command fails with only one parameter is also provided. Signed-off-by: Carlos Rica Signed-off-by: Junio C Hamano --- Documentation/git-config.txt | 2 +- builtin-config.c | 2 +- t/t1300-repo-config.sh | 9 ++++++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index 7d140073b1..8b94f19b17 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -11,7 +11,7 @@ SYNOPSIS [verse] 'git config' [] [type] [-z|--null] name [value [value_regex]] 'git config' [] [type] --add name value -'git config' [] [type] --replace-all name [value [value_regex]] +'git config' [] [type] --replace-all name value [value_regex] 'git config' [] [type] [-z|--null] --get name [value_regex] 'git config' [] [type] [-z|--null] --get-all name [value_regex] 'git config' [] [type] [-z|--null] --get-regexp name_regex [value_regex] diff --git a/builtin-config.c b/builtin-config.c index 1a3baa1f46..d8da72cf20 100644 --- a/builtin-config.c +++ b/builtin-config.c @@ -55,7 +55,7 @@ static struct option builtin_config_options[] = { OPT_BIT(0, "get", &actions, "get value: name [value-regex]", ACTION_GET), OPT_BIT(0, "get-all", &actions, "get all values: key [value-regex]", ACTION_GET_ALL), OPT_BIT(0, "get-regexp", &actions, "get values for regexp: name-regex [value-regex]", ACTION_GET_REGEXP), - OPT_BIT(0, "replace-all", &actions, "replace all matching variables: name [value [value_regex]", ACTION_REPLACE_ALL), + OPT_BIT(0, "replace-all", &actions, "replace all matching variables: name value [value_regex]", ACTION_REPLACE_ALL), OPT_BIT(0, "add", &actions, "adds a new variable: name value", ACTION_ADD), OPT_BIT(0, "unset", &actions, "removes a variable: name [value-regex]", ACTION_UNSET), OPT_BIT(0, "unset-all", &actions, "removes all matches: name [value-regex]", ACTION_UNSET_ALL), diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index 11b82f43dd..f0a75380b3 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -118,7 +118,14 @@ EOF test_expect_success 'multiple unset is correct' 'cmp .git/config expect' -mv .git/config2 .git/config +cp .git/config2 .git/config + +test_expect_success '--replace-all missing value' ' + test_must_fail git config --replace-all beta.haha && + test_cmp .git/config2 .git/config +' + +rm .git/config2 test_expect_success '--replace-all' \ 'git config --replace-all beta.haha gamma' From 236425919b6f2904ba34ed85bbde5e33ecc19f46 Mon Sep 17 00:00:00 2001 From: "David J. Mellor" Date: Mon, 16 Mar 2009 23:16:15 -0700 Subject: [PATCH 099/289] Documentation: reword the "Description" section of git-bisect.txt. Reword this section to make it less chatty. Also make minor grammatical fixes. Signed-off-by: David J. Mellor Signed-off-by: Junio C Hamano --- Documentation/git-bisect.txt | 154 +++++++++++++++++------------------ 1 file changed, 77 insertions(+), 77 deletions(-) diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt index 147ea38197..ef82bb8585 100644 --- a/Documentation/git-bisect.txt +++ b/Documentation/git-bisect.txt @@ -3,7 +3,7 @@ git-bisect(1) NAME ---- -git-bisect - Find the change that introduced a bug by binary search +git-bisect - Find by binary search the change that introduced a bug SYNOPSIS @@ -39,7 +39,8 @@ help" or "git bisect -h" to get a long usage description. Basic bisect commands: start, bad, good ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The way you use it is: +Using the Linux kernel tree as an example, basic use of the bisect +command is as follows: ------------------------------------------------ $ git bisect start @@ -48,61 +49,62 @@ $ git bisect good v2.6.13-rc2 # v2.6.13-rc2 was the last version # tested that was good ------------------------------------------------ -When you give at least one bad and one good versions, it will bisect -the revision tree and say something like: +When you have specified at least one bad and one good version, the +command bisects the revision tree and outputs something similar to: ------------------------------------------------ Bisecting: 675 revisions left to test after this ------------------------------------------------ -and check out the state in the middle. Now, compile that kernel, and -boot it. Now, let's say that this booted kernel works fine, then just -do +and then checks out the state in the middle. You would now compile +that kernel and boot it. If the booted kernel works correctly, you +would then issue the following command: ------------------------------------------------ $ git bisect good # this one is good ------------------------------------------------ -which will now say +which would then output something similar to: ------------------------------------------------ Bisecting: 337 revisions left to test after this ------------------------------------------------ and you continue along, compiling that one, testing it, and depending -on whether it is good or bad, you say "git bisect good" or "git bisect -bad", and ask for the next bisection. +on whether it is good or bad issuing the command "git bisect good" +or "git bisect bad" to ask for the next bisection. -Until you have no more left, and you'll have been left with the first -bad kernel rev in "refs/bisect/bad". +Eventually there will be no more revisions left to bisect, and you +will have been left with the first bad kernel revision in "refs/bisect/bad". Bisect reset ~~~~~~~~~~~~ -Oh, and then after you want to reset to the original head, do a +To return to the original head after a bisect session, you issue the +command: ------------------------------------------------ $ git bisect reset ------------------------------------------------ -to get back to the original branch, instead of being on the bisection -commit ("git bisect start" will do that for you too, actually: it will -reset the bisection state). +This resets the tree to the original branch instead of being on the +bisection commit ("git bisect start" will also do that, as it resets +the bisection state). Bisect visualize ~~~~~~~~~~~~~~~~ -During the bisection process, you can say +During the bisection process, you issue the command: ------------ $ git bisect visualize ------------ -to see the currently remaining suspects in 'gitk'. `visualize` is a bit -too long to type and `view` is provided as a synonym. +to see the currently remaining suspects in 'gitk'. `view` may also +be used as a synonym for `visualize`. -If 'DISPLAY' environment variable is not set, 'git log' is used -instead. You can even give command line options such as `-p` and +If the 'DISPLAY' environment variable is not set, 'git log' is used +instead. You can also give command line options such as `-p` and `--stat`. ------------ @@ -112,57 +114,56 @@ $ git bisect view --stat Bisect log and bisect replay ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The good/bad input is logged, and +The good/bad input is logged, and: ------------ $ git bisect log ------------ shows what you have done so far. You can truncate its output somewhere -and save it in a file, and run +and save it in a file, and run: ------------ $ git bisect replay that-file ------------ -if you find later you made a mistake telling good/bad about a -revision. +if you find later that you made a mistake specifying revisions as good/bad. -Avoiding to test a commit +Avoiding testing a commit ~~~~~~~~~~~~~~~~~~~~~~~~~ -If in a middle of bisect session, you know what the bisect suggested -to try next is not a good one to test (e.g. the change the commit +If in the middle of a bisect session, you know that the next suggested +revision is not a good one to test (e.g. the change the commit introduces is known not to work in your environment and you know it does not have anything to do with the bug you are chasing), you may -want to find a near-by commit and try that instead. +want to find a nearby commit and try that instead. -It goes something like this: +For example: ------------ $ git bisect good/bad # previous round was good/bad. Bisecting: 337 revisions left to test after this $ git bisect visualize # oops, that is uninteresting. -$ git reset --hard HEAD~3 # try 3 revs before what +$ git reset --hard HEAD~3 # try 3 revisions before what # was suggested ------------ -Then compile and test the one you chose to try. After that, tell -bisect what the result was as usual. +Then compile and test the chosen revision. Afterwards the revision +is marked as good/bad in the usual manner. Bisect skip ~~~~~~~~~~~~ -Instead of choosing by yourself a nearby commit, you may just want git -to do it for you using: +Instead of choosing by yourself a nearby commit, you can ask git +to do it for you by issuing the command: ------------ $ git bisect skip # Current version cannot be tested ------------ But computing the commit to test may be slower afterwards and git may -eventually not be able to tell the first bad among a bad and one or -more "skip"ped commits. +eventually not be able to tell the first bad commit among a bad commit +and one or more skipped commits. You can even skip a range of commits, instead of just one commit, using the "''..''" notation. For example: @@ -174,30 +175,29 @@ $ git bisect skip v2.5..v2.6 would mean that no commit between `v2.5` excluded and `v2.6` included can be tested. -Note that if you want to also skip the first commit of a range you can -use something like: +Note that if you also want to skip the first commit of the range you +would issue the command: ------------ $ git bisect skip v2.5 v2.5..v2.6 ------------ -and the commit pointed to by `v2.5` will be skipped too. +and the commit pointed to by `v2.5` would also be skipped. Cutting down bisection by giving more parameters to bisect start ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -You can further cut down the number of trials if you know what part of -the tree is involved in the problem you are tracking down, by giving -paths parameters when you say `bisect start`, like this: +You can further cut down the number of trials, if you know what part of +the tree is involved in the problem you are tracking down, by specifying +path parameters when issuing the `bisect start` command, like this: ------------ $ git bisect start -- arch/i386 include/asm-i386 ------------ -If you know beforehand more than one good commits, you can narrow the -bisect space down without doing the whole tree checkout every time you -give good commits. You give the bad revision immediately after `start` -and then you give all the good revisions you have: +If you know beforehand more than one good commit, you can narrow the +bisect space down by specifying all of the good commits immediately after +the bad commit when issuing the `bisect start` command: ------------ $ git bisect start v2.6.20-rc6 v2.6.20-rc4 v2.6.20-rc1 -- @@ -209,38 +209,38 @@ Bisect run ~~~~~~~~~~ If you have a script that can tell if the current source code is good -or bad, you can automatically bisect using: +or bad, you can bisect by issuing the command: ------------ $ git bisect run my_script ------------ -Note that the "run" script (`my_script` in the above example) should -exit with code 0 in case the current source code is good. Exit with a +Note that the script (`my_script` in the above example) should +exit with code 0 if the current source code is good, and exit with a code between 1 and 127 (inclusive), except 125, if the current source code is bad. -Any other exit code will abort the automatic bisect process. (A -program that does "exit(-1)" leaves $? = 255, see exit(3) manual page, -the value is chopped with "& 0377".) +Any other exit code will abort the bisect process. It should be noted +that a program that terminates via "exit(-1)" leaves $? = 255, (see the +exit(3) manual page), as the value is chopped with "& 0377". The special exit code 125 should be used when the current source code -cannot be tested. If the "run" script exits with this code, the current -revision will be skipped, see `git bisect skip` above. +cannot be tested. If the script exits with this code, the current +revision will be skipped (see `git bisect skip` above). -You may often find that during bisect you want to have near-constant -tweaks (e.g., s/#define DEBUG 0/#define DEBUG 1/ in a header file, or -"revision that does not have this commit needs this patch applied to -work around other problem this bisection is not interested in") -applied to the revision being tested. +You may often find that during a bisect session you want to have +temporary modifications (e.g. s/#define DEBUG 0/#define DEBUG 1/ in a +header file, or "revision that does not have this commit needs this +patch applied to work around another problem this bisection is not +interested in") applied to the revision being tested. To cope with such a situation, after the inner 'git bisect' finds the -next revision to test, with the "run" script, you can apply that tweak -before compiling, run the real test, and after the test decides if the -revision (possibly with the needed tweaks) passed the test, rewind the -tree to the pristine state. Finally the "run" script can exit with -the status of the real test to let the "git bisect run" command loop to -determine the outcome. +next revision to test, the script can apply the patch +before compiling, run the real test, and afterwards decide if the +revision (possibly with the needed patch) passed the test and then +rewind the tree to the pristine state. Finally the script should exit +with the status of the real test to let the "git bisect run" command loop +to determine the eventual outcome of the bisect session. EXAMPLES -------- @@ -257,39 +257,39 @@ $ git bisect run make # "make" builds the app ------------ $ cat ~/test.sh #!/bin/sh -make || exit 125 # this "skip"s broken builds +make || exit 125 # this skips broken builds make test # "make test" runs the test suite $ git bisect start v1.3 v1.1 -- # v1.3 is bad, v1.1 is good $ git bisect run ~/test.sh ------------ + Here we use a "test.sh" custom script. In this script, if "make" -fails, we "skip" the current commit. +fails, we skip the current commit. + -It's safer to use a custom script outside the repo to prevent +It is safer to use a custom script outside the repository to prevent interactions between the bisect, make and test processes and the script. + -And "make test" should "exit 0", if the test suite passes, and -"exit 1" (for example) otherwise. +"make test" should "exit 0", if the test suite passes, and +"exit 1" otherwise. * Automatically bisect a broken test case: + ------------ $ cat ~/test.sh #!/bin/sh -make || exit 125 # this "skip"s broken builds +make || exit 125 # this skips broken builds ~/check_test_case.sh # does the test case passes ? $ git bisect start HEAD HEAD~10 -- # culprit is among the last 10 $ git bisect run ~/test.sh ------------ + -Here "check_test_case.sh" should "exit 0", if the test case passes, -and "exit 1" (for example) otherwise. +Here "check_test_case.sh" should "exit 0" if the test case passes, +and "exit 1" otherwise. + -It's safer if both "test.sh" and "check_test_case.sh" scripts are -outside the repo to prevent interactions between the bisect, make and -test processes and the scripts. +It is safer if both "test.sh" and "check_test_case.sh" scripts are +outside the repository to prevent interactions between the bisect, +make and test processes and the scripts. Author ------ From b89510f024d228ad1c2f0fb4752157c18cc10d82 Mon Sep 17 00:00:00 2001 From: "David J. Mellor" Date: Mon, 16 Mar 2009 23:16:16 -0700 Subject: [PATCH 100/289] Documentation: minor grammatical fixes in git-blame.txt. Signed-off-by: David J. Mellor Signed-off-by: Junio C Hamano --- Documentation/blame-options.txt | 2 +- Documentation/git-blame.txt | 56 ++++++++++++++++----------------- Documentation/mailmap.txt | 17 +++++----- 3 files changed, 37 insertions(+), 38 deletions(-) diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt index df2a7c1641..26cfb62195 100644 --- a/Documentation/blame-options.txt +++ b/Documentation/blame-options.txt @@ -39,7 +39,7 @@ of lines before or after the line given by . Show raw timestamp (Default: off). -S :: - Use revs from revs-file instead of calling linkgit:git-rev-list[1]. + Use revisions from revs-file instead of calling linkgit:git-rev-list[1]. --reverse:: Walk history forward instead of backward. Instead of showing diff --git a/Documentation/git-blame.txt b/Documentation/git-blame.txt index 4ef54d6602..8c7b7b0838 100644 --- a/Documentation/git-blame.txt +++ b/Documentation/git-blame.txt @@ -18,9 +18,9 @@ DESCRIPTION Annotates each line in the given file with information from the revision which last modified the line. Optionally, start annotating from the given revision. -Also it can limit the range of lines annotated. +The command can also limit the range of lines annotated. -This report doesn't tell you anything about lines which have been deleted or +The report does not tell you anything about lines which have been deleted or replaced; you need to use a tool such as 'git-diff' or the "pickaxe" interface briefly mentioned in the following paragraph. @@ -48,26 +48,26 @@ include::blame-options.txt[] lines between files (see `-C`) and lines moved within a file (see `-M`). The first number listed is the score. This is the number of alphanumeric characters detected - to be moved between or within files. This must be above + as having been moved between or within files. This must be above a certain threshold for 'git-blame' to consider those lines of code to have been moved. -f:: --show-name:: - Show filename in the original commit. By default - filename is shown if there is any line that came from a - file with different name, due to rename detection. + Show the filename in the original commit. By default + the filename is shown if there is any line that came from a + file with a different name, due to rename detection. -n:: --show-number:: - Show line number in the original commit (Default: off). + Show the line number in the original commit (Default: off). -s:: - Suppress author name and timestamp from the output. + Suppress the author name and timestamp from the output. -w:: - Ignore whitespace when comparing parent's version and - child's to find where the lines came from. + Ignore whitespace when comparing the parent's version and + the child's to find where the lines came from. THE PORCELAIN FORMAT @@ -79,17 +79,17 @@ header at the minimum has the first line which has: - 40-byte SHA-1 of the commit the line is attributed to; - the line number of the line in the original file; - the line number of the line in the final file; -- on a line that starts a group of line from a different +- on a line that starts a group of lines from a different commit than the previous one, the number of lines in this group. On subsequent lines this field is absent. This header line is followed by the following information at least once for each commit: -- author name ("author"), email ("author-mail"), time +- the author name ("author"), email ("author-mail"), time ("author-time"), and timezone ("author-tz"); similarly for committer. -- filename in the commit the line is attributed to. +- the filename in the commit that the line is attributed to. - the first line of the commit log message ("summary"). The contents of the actual line is output after the above @@ -100,23 +100,23 @@ header elements later. SPECIFYING RANGES ----------------- -Unlike 'git-blame' and 'git-annotate' in older git, the extent -of annotation can be limited to both line ranges and revision +Unlike 'git-blame' and 'git-annotate' in older versions of git, the extent +of the annotation can be limited to both line ranges and revision ranges. When you are interested in finding the origin for -ll. 40-60 for file `foo`, you can use `-L` option like these +lines 40-60 for file `foo`, you can use the `-L` option like so (they mean the same thing -- both ask for 21 lines starting at line 40): git blame -L 40,60 foo git blame -L 40,+21 foo -Also you can use regular expression to specify the line range. +Also you can use a regular expression to specify the line range: git blame -L '/^sub hello {/,/^}$/' foo -would limit the annotation to the body of `hello` subroutine. +which limits the annotation to the body of the `hello` subroutine. -When you are not interested in changes older than the version +When you are not interested in changes older than version v2.6.18, or changes older than 3 weeks, you can use revision range specifiers similar to 'git-rev-list': @@ -129,7 +129,7 @@ commit v2.6.18 or the most recent commit that is more than 3 weeks old in the above example) are blamed for that range boundary commit. -A particularly useful way is to see if an added file have lines +A particularly useful way is to see if an added file has lines created by copy-and-paste from existing files. Sometimes this indicates that the developer was being sloppy and did not refactor the code properly. You can first find the commit that @@ -162,26 +162,26 @@ annotated. + Line numbers count from 1. -. The first time that commit shows up in the stream, it has various +. The first time that a commit shows up in the stream, it has various other information about it printed out with a one-word tag at the - beginning of each line about that "extended commit info" (author, - email, committer, dates, summary etc). + beginning of each line describing the extra commit information (author, + email, committer, dates, summary, etc.). -. Unlike Porcelain format, the filename information is always +. Unlike the Porcelain format, the filename information is always given and terminates the entry: "filename" + -and thus it's really quite easy to parse for some line- and word-oriented +and thus it is really quite easy to parse for some line- and word-oriented parser (which should be quite natural for most scripting languages). + [NOTE] For people who do parsing: to make it more robust, just ignore any -lines in between the first and last one ("" and "filename" lines) -where you don't recognize the tag-words (or care about that particular +lines between the first and last one ("" and "filename" lines) +where you do not recognize the tag words (or care about that particular one) at the beginning of the "extended information" lines. That way, if there is ever added information (like the commit encoding or extended -commit commentary), a blame viewer won't ever care. +commit commentary), a blame viewer will not care. MAPPING AUTHORS diff --git a/Documentation/mailmap.txt b/Documentation/mailmap.txt index e25b154838..288f04e70c 100644 --- a/Documentation/mailmap.txt +++ b/Documentation/mailmap.txt @@ -5,22 +5,21 @@ canonical real names and email addresses. In the simple form, each line in the file consists of the canonical real name of an author, whitespace, and an email address used in the -commit (enclosed by '<' and '>') to map to the name. Thus, looks like -this +commit (enclosed by '<' and '>') to map to the name. For example: -- Proper Name -- -The more complex forms are +The more complex forms are: -- -- -which allows mailmap to replace only the email part of a commit, and +which allows mailmap to replace only the email part of a commit, and: -- Proper Name -- which allows mailmap to replace both the name and the email of a -commit matching the specified commit email address, and +commit matching the specified commit email address, and: -- Proper Name Commit Name -- @@ -47,8 +46,8 @@ Jane Doe Joe R. Developer ------------ -Note how we don't need an entry for , because the -real name of that author is correct already. +Note how there is no need for an entry for , because the +real name of that author is already correct. Example 2: Your repository contains commits from the following authors: @@ -62,7 +61,7 @@ claus CTO ------------ -Then, you might want a `.mailmap` file looking like: +Then you might want a `.mailmap` file that looks like: ------------ Some Dude nick1 @@ -72,4 +71,4 @@ Santa Claus ------------ Use hash '#' for comments that are either on their own line, or after -the email address. \ No newline at end of file +the email address. From e5ac1217eb2639df1d39fdcd249a35d99e8a7a8e Mon Sep 17 00:00:00 2001 From: "David J. Mellor" Date: Mon, 16 Mar 2009 23:16:17 -0700 Subject: [PATCH 101/289] Documentation: minor grammatical fixes in git-branch.txt. Signed-off-by: David J. Mellor Signed-off-by: Junio C Hamano --- Documentation/git-branch.txt | 50 ++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index 6103d62fe3..7f7b781f24 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -18,19 +18,19 @@ SYNOPSIS DESCRIPTION ----------- -With no arguments, existing branches are listed, the current branch will +With no arguments, existing branches are listed and the current branch will be highlighted with an asterisk. Option `-r` causes the remote-tracking branches to be listed, and option `-a` shows both. -With `--contains`, shows only the branches that contains the named commit -(in other words, the branches whose tip commits are descendant of the +With `--contains`, shows only the branches that contain the named commit +(in other words, the branches whose tip commits are descendants of the named commit). With `--merged`, only branches merged into the named commit (i.e. the branches whose tip commits are reachable from the named commit) will be listed. With `--no-merged` only branches not merged into -the named commit will be listed. Missing argument defaults to -'HEAD' (i.e. the tip of the current branch). +the named commit will be listed. If the argument is missing it +defaults to 'HEAD' (i.e. the tip of the current branch). -In its second form, a new branch named will be created. +In the command's second form, a new branch named will be created. It will start out with a head equal to the one given as . If no is given, the branch will be created with a head equal to that of the currently checked out branch. @@ -57,9 +57,9 @@ has a reflog then the reflog will also be deleted. Use -r together with -d to delete remote-tracking branches. Note, that it only makes sense to delete remote-tracking branches if they no longer exist -in remote repository or if 'git-fetch' was configured not to fetch -them again. See also 'prune' subcommand of linkgit:git-remote[1] for way to -clean up all obsolete remote-tracking branches. +in the remote repository or if 'git-fetch' was configured not to fetch +them again. See also the 'prune' subcommand of linkgit:git-remote[1] for a +way to clean up all obsolete remote-tracking branches. OPTIONS @@ -83,7 +83,7 @@ OPTIONS Move/rename a branch and the corresponding reflog. -M:: - Move/rename a branch even if the new branchname already exists. + Move/rename a branch even if the new branch name already exists. --color:: Color branches to highlight current, local, and remote branches. @@ -103,17 +103,17 @@ OPTIONS Show sha1 and commit subject line for each head. --abbrev=:: - Alter minimum display length for sha1 in output listing, - default value is 7. + Alter the sha1's minimum display length in the output listing. + The default value is 7. --no-abbrev:: - Display the full sha1s in output listing rather than abbreviating them. + Display the full sha1s in the output listing rather than abbreviating them. --track:: - When creating a new branch, set up configuration so that 'git-pull' + When creating a new branch, set up the configuration so that 'git-pull' will automatically retrieve data from the start point, which must be a branch. Use this if you always pull from the same upstream branch - into the new branch, and if you don't want to use "git pull + into the new branch, and if you do not want to use "git pull " explicitly. This behavior is the default when the start point is a remote branch. Set the branch.autosetupmerge configuration variable to `false` if you want @@ -149,13 +149,13 @@ OPTIONS :: The new name for an existing branch. The same restrictions as for - applies. + apply. Examples -------- -Start development off of a known tag:: +Start development from a known tag:: + ------------ $ git clone git://git.kernel.org/pub/scm/.../linux-2.6 my2.6 @@ -167,7 +167,7 @@ $ git checkout my2.6.14 <1> This step and the next one could be combined into a single step with "checkout -b my2.6.14 v2.6.14". -Delete unneeded branch:: +Delete an unneeded branch:: + ------------ $ git clone git://git.kernel.org/.../git.git my.git @@ -176,21 +176,21 @@ $ git branch -d -r origin/todo origin/html origin/man <1> $ git branch -D test <2> ------------ + -<1> Delete remote-tracking branches "todo", "html", "man". Next 'fetch' or -'pull' will create them again unless you configure them not to. See -linkgit:git-fetch[1]. -<2> Delete "test" branch even if the "master" branch (or whichever branch is -currently checked out) does not have all commits from test branch. +<1> Delete the remote-tracking branches "todo", "html" and "man". The next +'fetch' or 'pull' will create them again unless you configure them not to. +See linkgit:git-fetch[1]. +<2> Delete the "test" branch even if the "master" branch (or whichever branch +is currently checked out) does not have all commits from the test branch. Notes ----- -If you are creating a branch that you want to immediately checkout, it's +If you are creating a branch that you want to checkout immediately, it is easier to use the git checkout command with its `-b` option to create a branch and check it out with a single command. -The options `--contains`, `--merged` and `--no-merged` serves three related +The options `--contains`, `--merged` and `--no-merged` serve three related but different purposes: - `--contains ` is used to find all branches which will need From 381b920b8ac1440962f340cba9030e2dc3130c49 Mon Sep 17 00:00:00 2001 From: Kjetil Barvik Date: Tue, 17 Mar 2009 19:20:29 +0100 Subject: [PATCH 102/289] Revert "lstat_cache(): print a warning if doing ping-pong between cache types" This reverts commit 7734f04873cfaddd0b148074a633f1f824fd961f. I guess that the reverted commit, 7734f048, has been in test long enough, and should now be reverted. I have not received any info regarding any debug output of the reverted commit, so lets hope that the lstat_cache() function do not cause any ping-pong. Signed-off-by: Kjetil Barvik Signed-off-by: Junio C Hamano --- symlinks.c | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/symlinks.c b/symlinks.c index cb255a3187..1d6b35b858 100644 --- a/symlinks.c +++ b/symlinks.c @@ -51,11 +51,6 @@ static inline void reset_lstat_cache(void) */ } -#define SWITCHES_BEFORE_WARNING 10 -static unsigned int cache_switches, number_of_warnings; -static unsigned int current_cache_func, last_cache_func; -static unsigned int total_calls; - #define FL_DIR (1 << 0) #define FL_NOENT (1 << 1) #define FL_SYMLINK (1 << 2) @@ -82,7 +77,6 @@ static int lstat_cache(const char *name, int len, int match_flags, ret_flags, save_flags, max_len, ret; struct stat st; - total_calls++; if (cache.track_flags != track_flags || cache.prefix_len_stat_func != prefix_len_stat_func) { /* @@ -94,17 +88,6 @@ static int lstat_cache(const char *name, int len, cache.track_flags = track_flags; cache.prefix_len_stat_func = prefix_len_stat_func; match_len = last_slash = 0; - cache_switches++; - if (cache_switches > SWITCHES_BEFORE_WARNING) { - if (number_of_warnings < 10 || number_of_warnings % 1000 == 0) - printf("warning from %s:%d cache_switches:%u > %u "\ - "(current:%u last:%u total:%u)\n", - __FILE__, __LINE__, - cache_switches, SWITCHES_BEFORE_WARNING, - current_cache_func, last_cache_func, - total_calls); - number_of_warnings++; - } } else { /* * Check to see if we have a match from the cache for @@ -233,8 +216,6 @@ void clear_lstat_cache(void) */ int has_symlink_leading_path(const char *name, int len) { - last_cache_func = current_cache_func; - current_cache_func = 1; return lstat_cache(name, len, FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) & FL_SYMLINK; @@ -246,8 +227,6 @@ int has_symlink_leading_path(const char *name, int len) */ int has_symlink_or_noent_leading_path(const char *name, int len) { - last_cache_func = current_cache_func; - current_cache_func = 2; return lstat_cache(name, len, FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT) & (FL_SYMLINK|FL_NOENT); @@ -262,8 +241,6 @@ int has_symlink_or_noent_leading_path(const char *name, int len) */ int has_dirs_only_path(const char *name, int len, int prefix_len) { - last_cache_func = current_cache_func; - current_cache_func = 3; return lstat_cache(name, len, FL_DIR|FL_FULLPATH, prefix_len) & FL_DIR; From d42ec126aa717d00549e387d5a95fd55683c2e2c Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Tue, 17 Mar 2009 17:22:53 +0100 Subject: [PATCH 103/289] disable post-checkout test on Cygwin It is broken because of the tricks we have to play with lstat to get the bearable perfomance out of the call. Sadly, it disables access to Cygwin's executable attribute, which Windows filesystems do not have at all. Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- t/t5403-post-checkout-hook.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/t/t5403-post-checkout-hook.sh b/t/t5403-post-checkout-hook.sh index 4fdb418550..5858b868ed 100755 --- a/t/t5403-post-checkout-hook.sh +++ b/t/t5403-post-checkout-hook.sh @@ -71,6 +71,7 @@ test_expect_success 'post-checkout receives the right args when not switching br test $old = $new -a $flag = 0 ' +if test "$(git config --bool core.filemode)" = true; then mkdir -p templates/hooks cat >templates/hooks/post-checkout <<'EOF' #!/bin/sh @@ -82,5 +83,6 @@ test_expect_success 'post-checkout hook is triggered by clone' ' git clone --template=templates . clone3 && test -f clone3/.git/post-checkout.args ' +fi test_done From d6aba61f88dafc10cfb874b91e7864419fa81fd7 Mon Sep 17 00:00:00 2001 From: Chris Johnsen Date: Sat, 14 Mar 2009 21:32:01 -0500 Subject: [PATCH 104/289] git-push.txt: describe how to default to pushing only current branch Signed-off-by: Chris Johnsen Signed-off-by: Junio C Hamano --- Documentation/git-push.txt | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index 4e7e5a719a..fd53c49fb8 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -24,8 +24,8 @@ every time you push into it, by setting up 'hooks' there. See documentation for linkgit:git-receive-pack[1]. -OPTIONS -------- +OPTIONS[[OPTIONS]] +------------------ :: The "remote" repository that is destination of a push operation. This parameter can be either a URL @@ -187,6 +187,28 @@ reason:: Examples -------- +git push:: + Works like `git push `, where is the + current branch's remote (or `origin`, if no remote is + configured for the current branch). + +git push origin:: + Without additional configuration, works like + `git push origin :`. ++ +The default behavior of this command when no is given can be +configured by setting the `push` option of the remote. ++ +For example, to default to pushing only the current branch to `origin` +use `git config remote.origin.push HEAD`. Any valid (like +the ones in the examples below) can be configured as the default for +`git push origin`. + +git push origin ::: + Push "matching" branches to `origin`. See + in the <> section above for a + description of "matching" branches. + git push origin master:: Find a ref that matches `master` in the source repository (most likely, it would find `refs/heads/master`), and update From dcb11263bcdbf31955dd00777392249bf1624226 Mon Sep 17 00:00:00 2001 From: Chris Johnsen Date: Sun, 15 Mar 2009 06:30:52 -0500 Subject: [PATCH 105/289] Documentation: remove extra quoting/emphasis around literal texts If literal text (asciidoc `...`) can be rendered in a differently from normal text for each output format (man, HTML), then we do not need extra quotes or other wrapping around inline literal text segments. config.txt Change '`...`' to `...`. In asciidoc, the single quotes provide emphasis, literal text should be distintive enough. Change "`...`" to `...`. These double quotes do not work if present in the described config value, so drop them. git-checkout.txt Change "`...`" to `...` or `"..."`. All instances are command line argument examples. One "`-`" becomes `-`. Two others are involve curly braces, so move the double quotes inside the literal region to indicate that they might need to be quoted on the command line of certain shells (tcsh). git-merge.txt Change "`...`" to `...`. All instances are used to describe merge conflict markers. The quotes should are not important. git-rev-parse.txt Change "`...`" to `...`. All instances are around command line arguments where no in-shell quoting should be necessary. gitcli.txt Change `"..."` to `...`. All instances are around command line examples or single command arguments. They do not semanticly belong inside the literal text, and they are not needed outside it. glossary-content.txt user-manual.txt Change "`...`" to `...`. All instances were around command lines. Signed-off-by: Chris Johnsen Signed-off-by: Junio C Hamano --- Documentation/config.txt | 24 ++++++++++++------------ Documentation/git-checkout.txt | 4 ++-- Documentation/git-merge.txt | 6 +++--- Documentation/git-rev-parse.txt | 8 ++++---- Documentation/gitcli.txt | 24 ++++++++++++------------ Documentation/glossary-content.txt | 2 +- Documentation/user-manual.txt | 6 +++--- 7 files changed, 37 insertions(+), 37 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 56bd781a16..70fd172c6b 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -25,7 +25,7 @@ blank lines are ignored. The file consists of sections and variables. A section begins with the name of the section in square brackets and continues until the next section begins. Section names are not case sensitive. Only alphanumeric -characters, '`-`' and '`.`' are allowed in section names. Each variable +characters, `-` and `.` are allowed in section names. Each variable must belong to some section, which means that there must be section header before first setting of a variable. @@ -39,7 +39,7 @@ in the section header, like in example below: -------- Subsection names can contain any characters except newline (doublequote -'`"`' and backslash have to be escaped as '`\"`' and '`\\`', +`"` and backslash have to be escaped as `\"` and `\\`, respectively) and are case sensitive. Section header cannot span multiple lines. Variables may belong directly to a section or to a given subsection. You can have `[section]` if you have `[section "subsection"]`, but you @@ -53,7 +53,7 @@ All the other lines are recognized as setting variables, in the form 'name = value'. If there is no equal sign on the line, the entire line is taken as 'name' and the variable is recognized as boolean "true". The variable names are case-insensitive and only alphanumeric -characters and '`-`' are allowed. There can be more than one value +characters and `-` are allowed. There can be more than one value for a given variable; we say then that variable is multivalued. Leading and trailing whitespace in a variable value is discarded. @@ -69,15 +69,15 @@ String values may be entirely or partially enclosed in double quotes. You need to enclose variable value in double quotes if you want to preserve leading or trailing whitespace, or if variable value contains beginning of comment characters (if it contains '#' or ';'). -Double quote '`"`' and backslash '`\`' characters in variable value must -be escaped: use '`\"`' for '`"`' and '`\\`' for '`\`'. +Double quote `"` and backslash `\` characters in variable value must +be escaped: use `\"` for `"` and `\\` for `\`. -The following escape sequences (beside '`\"`' and '`\\`') are recognized: -'`\n`' for newline character (NL), '`\t`' for horizontal tabulation (HT, TAB) -and '`\b`' for backspace (BS). No other char escape sequence, nor octal +The following escape sequences (beside `\"` and `\\`) are recognized: +`\n` for newline character (NL), `\t` for horizontal tabulation (HT, TAB) +and `\b` for backspace (BS). No other char escape sequence, nor octal char sequences are valid. -Variable value ending in a '`\`' is continued on the next line in the +Variable value ending in a `\` is continued on the next line in the customary UNIX fashion. Some variables may require special value format. @@ -382,9 +382,9 @@ core.pager:: to override git's default settings this way, you need to be explicit. For example, to disable the S option in a backward compatible manner, set `core.pager` - to "`less -+$LESS -FRX`". This will be passed to the + to `less -+$LESS -FRX`. This will be passed to the shell by git, which will translate the final command to - "`LESS=FRSX less -+FRSX -FRX`". + `LESS=FRSX less -+FRSX -FRX`. core.whitespace:: A comma separated list of common whitespace problems to @@ -1161,7 +1161,7 @@ pager.:: particular git subcommand when writing to a tty. If `\--paginate` or `\--no-pager` is specified on the command line, it takes precedence over this option. To disable pagination for - all commands, set `core.pager` or 'GIT_PAGER' to "`cat`". + all commands, set `core.pager` or `GIT_PAGER` to `cat`. pull.octopus:: The default merge strategy to use when pulling multiple branches diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 125d8f3c32..1a6c19e5c3 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -133,9 +133,9 @@ the conflicted merge in the specified paths. When this parameter names a non-branch (but still a valid commit object), your HEAD becomes 'detached'. + -As a special case, the "`@\{-N\}`" syntax for the N-th last branch +As a special case, the `"@\{-N\}"` syntax for the N-th last branch checks out the branch (instead of detaching). You may also specify -"`-`" which is synonymous with "`@\{-1\}`". +`-` which is synonymous with `"@\{-1\}"`. Detached HEAD diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt index f7be5846a6..cc0d30fe7e 100644 --- a/Documentation/git-merge.txt +++ b/Documentation/git-merge.txt @@ -146,7 +146,7 @@ And here is another line that is cleanly resolved or unmodified. ------------ The area where a pair of conflicting changes happened is marked with markers -"`<<<<<<<`", "`=======`", and "`>>>>>>>`". The part before the "`=======`" +`<<<<<<<`, `=======`, and `>>>>>>>`. The part before the `=======` is typically your side, and the part afterwards is typically their side. The default format does not show what the original said in the conflicting @@ -173,8 +173,8 @@ Git makes conflict resolution easy. And here is another line that is cleanly resolved or unmodified. ------------ -In addition to the "`<<<<<<<`", "`=======`", and "`>>>>>>>`" markers, it uses -another "`|||||||`" marker that is followed by the original text. You can +In addition to the `<<<<<<<`, `=======`, and `>>>>>>>` markers, it uses +another `|||||||` marker that is followed by the original text. You can tell that the original just stated a fact, and your side simply gave in to that statement and gave up, while the other side tried to have a more positive attitude. You can sometimes come up with a better resolution by diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index 3ccef2f2b3..5ed2bc840f 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -299,18 +299,18 @@ previous section means the set of commits reachable from that commit, following the commit ancestry chain. To exclude commits reachable from a commit, a prefix `{caret}` -notation is used. E.g. "`{caret}r1 r2`" means commits reachable +notation is used. E.g. `{caret}r1 r2` means commits reachable from `r2` but exclude the ones reachable from `r1`. This set operation appears so often that there is a shorthand for it. When you have two commits `r1` and `r2` (named according to the syntax explained in SPECIFYING REVISIONS above), you can ask for commits that are reachable from r2 excluding those that are reachable -from r1 by "`{caret}r1 r2`" and it can be written as "`r1..r2`". +from r1 by `{caret}r1 r2` and it can be written as `r1..r2`. -A similar notation "`r1\...r2`" is called symmetric difference +A similar notation `r1\...r2` is called symmetric difference of `r1` and `r2` and is defined as -"`r1 r2 --not $(git merge-base --all r1 r2)`". +`r1 r2 --not $(git merge-base --all r1 r2)`. It is the set of commits that are reachable from either one of `r1` or `r2` but not from both. diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt index 29e5929db2..be39ed7c15 100644 --- a/Documentation/gitcli.txt +++ b/Documentation/gitcli.txt @@ -46,20 +46,20 @@ Here are the rules regarding the "flags" that you should follow when you are scripting git: * it's preferred to use the non dashed form of git commands, which means that - you should prefer `"git foo"` to `"git-foo"`. + you should prefer `git foo` to `git-foo`. - * splitting short options to separate words (prefer `"git foo -a -b"` - to `"git foo -ab"`, the latter may not even work). + * splitting short options to separate words (prefer `git foo -a -b` + to `git foo -ab`, the latter may not even work). * when a command line option takes an argument, use the 'sticked' form. In - other words, write `"git foo -oArg"` instead of `"git foo -o Arg"` for short - options, and `"git foo --long-opt=Arg"` instead of `"git foo --long-opt Arg"` + other words, write `git foo -oArg` instead of `git foo -o Arg` for short + options, and `git foo --long-opt=Arg` instead of `git foo --long-opt Arg` for long options. An option that takes optional option-argument must be written in the 'sticked' form. * when you give a revision parameter to a command, make sure the parameter is not ambiguous with a name of a file in the work tree. E.g. do not write - `"git log -1 HEAD"` but write `"git log -1 HEAD --"`; the former will not work + `git log -1 HEAD` but write `git log -1 HEAD --`; the former will not work if you happen to have a file called `HEAD` in the work tree. @@ -99,17 +99,17 @@ usage: git-describe [options] * Negating options ~~~~~~~~~~~~~~~~ -Options with long option names can be negated by prefixing `"--no-"`. For -example, `"git branch"` has the option `"--track"` which is 'on' by default. You -can use `"--no-track"` to override that behaviour. The same goes for `"--color"` -and `"--no-color"`. +Options with long option names can be negated by prefixing `--no-`. For +example, `git branch` has the option `--track` which is 'on' by default. You +can use `--no-track` to override that behaviour. The same goes for `--color` +and `--no-color`. Aggregating short options ~~~~~~~~~~~~~~~~~~~~~~~~~ Commands that support the enhanced option parser allow you to aggregate short -options. This means that you can for example use `"git rm -rf"` or -`"git clean -fdx"`. +options. This means that you can for example use `git rm -rf` or +`git clean -fdx`. Separating argument from the option diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt index 9afca755ed..4fc1cf1184 100644 --- a/Documentation/glossary-content.txt +++ b/Documentation/glossary-content.txt @@ -262,7 +262,7 @@ This commit is referred to as a "merge commit", or sometimes just a 'origin' is used for that purpose. New upstream updates will be fetched into remote <> named origin/name-of-upstream-branch, which you can see using - "`git branch -r`". + `git branch -r`. [[def_pack]]pack:: A set of objects which have been compressed into one file (to save space diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index 96af8977f6..e33b29b1dd 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -1136,10 +1136,10 @@ Ignoring files A project will often generate files that you do 'not' want to track with git. This typically includes files generated by a build process or temporary backup files made by your editor. Of course, 'not' tracking files with git -is just a matter of 'not' calling "`git-add`" on them. But it quickly becomes +is just a matter of 'not' calling `git-add` on them. But it quickly becomes annoying to have these untracked files lying around; e.g. they make -"`git add .`" practically useless, and they keep showing up in the output of -"`git status`". +`git add .` practically useless, and they keep showing up in the output of +`git status`. You can tell git to ignore certain files by creating a file called .gitignore in the top level of your working directory, with contents such as: From 188c3827c10f5a04588ef7af8b63a7b5dbcc38ed Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Mon, 16 Mar 2009 21:18:42 +0100 Subject: [PATCH 106/289] Tests: use test_cmp instead of diff where possible Several old tests were written before test_cmp was introduced, convert these to test_cmp. If were are at it, fix the order of the arguments where necessary to make expected come first, so the command shows how the test result deviates from the correct output. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- t/t0000-basic.sh | 8 ++++---- t/t1100-commit-tree-options.sh | 2 +- t/t1400-update-ref.sh | 6 +++--- t/t3000-ls-files-others.sh | 4 ++-- t/t3010-ls-files-killed-modified.sh | 4 ++-- t/t5000-tar-tree.sh | 22 +++++++++++----------- t/t9001-send-email.sh | 2 +- 7 files changed, 24 insertions(+), 24 deletions(-) diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh index 70df15cbd8..ddcd5b0efb 100755 --- a/t/t0000-basic.sh +++ b/t/t0000-basic.sh @@ -127,7 +127,7 @@ cat >expected <<\EOF EOF test_expect_success \ 'validate git ls-files output for a known tree.' \ - 'diff current expected' + 'test_cmp expected current' test_expect_success \ 'writing tree out with git write-tree.' \ @@ -147,7 +147,7 @@ cat >expected <<\EOF EOF test_expect_success \ 'git ls-tree output for a known tree.' \ - 'diff current expected' + 'test_cmp expected current' # This changed in ls-tree pathspec change -- recursive does # not show tree nodes anymore. @@ -166,7 +166,7 @@ cat >expected <<\EOF EOF test_expect_success \ 'git ls-tree -r output for a known tree.' \ - 'diff current expected' + 'test_cmp expected current' # But with -r -t we can have both. test_expect_success \ @@ -187,7 +187,7 @@ cat >expected <<\EOF EOF test_expect_success \ 'git ls-tree -r output for a known tree.' \ - 'diff current expected' + 'test_cmp expected current' test_expect_success \ 'writing partial tree out with git write-tree --prefix.' \ diff --git a/t/t1100-commit-tree-options.sh b/t/t1100-commit-tree-options.sh index 7f7fc36734..c4414ff576 100755 --- a/t/t1100-commit-tree-options.sh +++ b/t/t1100-commit-tree-options.sh @@ -40,6 +40,6 @@ test_expect_success \ test_expect_success \ 'compare commit' \ - 'diff expected commit' + 'test_cmp expected commit' test_done diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index bd589268fc..54ba3df95f 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -137,7 +137,7 @@ $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150860 +0000 EOF test_expect_success \ "verifying $m's log" \ - "diff expect .git/logs/$m" + "test_cmp expect .git/logs/$m" rm -rf .git/$m .git/logs expect test_expect_success \ @@ -168,7 +168,7 @@ $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 +0000 EOF test_expect_success \ "verifying $m's log" \ - 'diff expect .git/logs/$m' + 'test_cmp expect .git/logs/$m' rm -f .git/$m .git/logs/$m expect git update-ref $m $D @@ -272,7 +272,7 @@ $h_FIXED $h_MERGED $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117151100 +0000 c EOF test_expect_success \ 'git commit logged updates' \ - "diff expect .git/logs/$m" + "test_cmp expect .git/logs/$m" unset h_TEST h_OTHER h_FIXED h_MERGED test_expect_success \ diff --git a/t/t3000-ls-files-others.sh b/t/t3000-ls-files-others.sh index bc0a351392..36eee0f8ae 100755 --- a/t/t3000-ls-files-others.sh +++ b/t/t3000-ls-files-others.sh @@ -42,7 +42,7 @@ test_expect_success \ test_expect_success \ 'git ls-files --others should pick up symlinks.' \ - 'diff output expected1' + 'test_cmp expected1 output' test_expect_success \ 'git ls-files --others --directory to show output.' \ @@ -51,6 +51,6 @@ test_expect_success \ test_expect_success \ 'git ls-files --others --directory should not get confused.' \ - 'diff output expected2' + 'test_cmp expected2 output' test_done diff --git a/t/t3010-ls-files-killed-modified.sh b/t/t3010-ls-files-killed-modified.sh index ec14040637..e4f02a0968 100755 --- a/t/t3010-ls-files-killed-modified.sh +++ b/t/t3010-ls-files-killed-modified.sh @@ -75,7 +75,7 @@ EOF test_expect_success \ 'validate git ls-files -k output.' \ - 'diff .output .expected' + 'test_cmp .expected .output' test_expect_success \ 'git ls-files -m to show modified files.' \ @@ -91,6 +91,6 @@ EOF test_expect_success \ 'validate git ls-files -m output.' \ - 'diff .output .expected' + 'test_cmp .expected .output' test_done diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index b7e362834b..e1ed073f95 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -76,7 +76,7 @@ test_expect_success \ test_expect_success \ 'git archive vs. git tar-tree' \ - 'diff b.tar b2.tar' + 'test_cmp b.tar b2.tar' test_expect_success \ 'git archive in a bare repo' \ @@ -96,12 +96,12 @@ test_expect_success \ "$TAR" xf b.tar -C extract a/a && test-chmtime -v +0 extract/a/a |cut -f 1 >b.mtime && echo "1117231200" >expected.mtime && - diff expected.mtime b.mtime' + test_cmp expected.mtime b.mtime' test_expect_success \ 'git get-tar-commit-id' \ 'git get-tar-commit-id b.commitid && - diff .git/$(git symbolic-ref HEAD) b.commitid' + test_cmp .git/$(git symbolic-ref HEAD) b.commitid' test_expect_success \ 'extract tar archive' \ @@ -110,7 +110,7 @@ test_expect_success \ test_expect_success \ 'validate filenames' \ '(cd b/a && find .) | sort >b.lst && - diff a.lst b.lst' + test_cmp a.lst b.lst' test_expect_success \ 'validate file contents' \ @@ -127,7 +127,7 @@ test_expect_success \ test_expect_success \ 'validate filenames with prefix' \ '(cd c/prefix/a && find .) | sort >c.lst && - diff a.lst c.lst' + test_cmp a.lst c.lst' test_expect_success \ 'validate file contents with prefix' \ @@ -148,8 +148,8 @@ test_expect_success \ 'validate substfile contents' \ 'git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \ >f/a/substfile1.expected && - diff f/a/substfile1.expected f/a/substfile1 && - diff a/substfile2 f/a/substfile2 + test_cmp f/a/substfile1.expected f/a/substfile1 && + test_cmp a/substfile2 f/a/substfile2 ' test_expect_success \ @@ -160,8 +160,8 @@ test_expect_success \ 'validate substfile contents from archive with prefix' \ 'git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \ >g/prefix/a/substfile1.expected && - diff g/prefix/a/substfile1.expected g/prefix/a/substfile1 && - diff a/substfile2 g/prefix/a/substfile2 + test_cmp g/prefix/a/substfile1.expected g/prefix/a/substfile1 && + test_cmp a/substfile2 g/prefix/a/substfile2 ' test_expect_success \ @@ -194,7 +194,7 @@ test_expect_success \ test_expect_success \ 'validate filenames' \ '(cd d/a && find .) | sort >d.lst && - diff a.lst d.lst' + test_cmp a.lst d.lst' test_expect_success \ 'validate file contents' \ @@ -211,7 +211,7 @@ test_expect_success \ test_expect_success \ 'validate filenames with prefix' \ '(cd e/prefix/a && find .) | sort >e.lst && - diff a.lst e.lst' + test_cmp a.lst e.lst' test_expect_success \ 'validate file contents with prefix' \ diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index 08d5b91c91..9523305304 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -88,7 +88,7 @@ cat >expected <<\EOF EOF test_expect_success \ 'Verify commandline' \ - 'diff commandline1 expected' + 'test_cmp expected commandline1' cat >expected-show-all-headers <<\EOF 0001-Second.patch From 01d386121758ac72aedd5f268feb763d53fc3cdf Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 16 Mar 2009 00:44:57 -0700 Subject: [PATCH 107/289] git-send-email.txt: describe --compose better Signed-off-by: Stephen Boyd Signed-off-by: Junio C Hamano --- Documentation/git-send-email.txt | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index 14dfb501eb..10dfd667b2 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -60,14 +60,13 @@ The --cc option must be repeated for each user you want on the cc list. Use $GIT_EDITOR, core.editor, $VISUAL, or $EDITOR to edit an introductory message for the patch series. + -When '--compose' is used, git send-email gets less interactive will use the -values of the headers you set there. If the body of the email (what you type -after the headers and a blank line) only contains blank (or GIT: prefixed) -lines, the summary won't be sent, but git-send-email will still use the -Headers values if you don't removed them. +When '--compose' is used, git send-email will use the From, Subject, and +In-Reply-To headers specified in the message. If the body of the message +(what you type after the headers and a blank line) only contains blank +(or GIT: prefixed) lines the summary won't be sent, but From, Subject, +and In-Reply-To headers will be used unless they are removed. + -If it wasn't able to see a header in the summary it will ask you about it -interactively after quitting your editor. +Missing From or In-Reply-To headers will be prompted for. --from:: Specify the sender of the emails. This will default to From 521537476fe99b97bfcdf1b8f0c579061af5fd3e Mon Sep 17 00:00:00 2001 From: Finn Arne Gangstad Date: Mon, 16 Mar 2009 16:42:51 +0100 Subject: [PATCH 108/289] New config push.default to decide default behavior for push When "git push" is not told what refspecs to push, it pushes all matching branches to the current remote. For some workflows this default is not useful, and surprises new users. Some have even found that this default behaviour is too easy to trigger by accident with unwanted consequences. Introduce a new configuration variable "push.default" that decides what action git push should take if no refspecs are given or implied by the command line arguments or the current remote configuration. Possible values are: 'nothing' : Push nothing; 'matching' : Current default behaviour, push all branches that already exist in the current remote; 'tracking' : Push the current branch to whatever it is tracking; 'current' : Push the current branch to a branch of the same name, i.e. HEAD. Signed-off-by: Finn Arne Gangstad Signed-off-by: Junio C Hamano --- Documentation/config.txt | 18 ++++++++++++++ builtin-push.c | 53 ++++++++++++++++++++++++++++++++++++---- cache.h | 9 +++++++ config.c | 28 +++++++++++++++++++++ environment.c | 1 + 5 files changed, 104 insertions(+), 5 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index f5152c5038..7b2b3f44aa 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -1160,6 +1160,24 @@ pull.octopus:: pull.twohead:: The default merge strategy to use when pulling a single branch. +push.default:: + Defines the action git push should take if no refspec is given + on the command line, no refspec is configured in the remote, and + no refspec is implied by any of the options given on the command + line. ++ +The term `current remote` means the remote configured for the current +branch, or `origin` if no remote is configured. `origin` is also used +if you are not on any branch. Possible values are: ++ +* `nothing` do not push anything. +* `matching` push all matching branches to the current remote. + All branches having the same name in both ends are considered to be + matching. This is the current default value. +* `tracking` push the current branch to the branch it is tracking. +* `current` push the current branch to a branch of the same name on the + current remote. + receive.fsckObjects:: If it is set to true, git-receive-pack will check all received objects. It will abort in the case of a malformed object or a diff --git a/builtin-push.c b/builtin-push.c index 122fdcfbdc..45fe843b20 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -48,6 +48,48 @@ static void set_refspecs(const char **refs, int nr) } } +static void setup_push_tracking(void) +{ + struct strbuf refspec = STRBUF_INIT; + struct branch *branch = branch_get(NULL); + if (!branch) + die("You are not currently on a branch."); + if (!branch->merge_nr) + die("The current branch %s is not tracking anything.", + branch->name); + if (branch->merge_nr != 1) + die("The current branch %s is tracking multiple branches, " + "refusing to push.", branch->name); + strbuf_addf(&refspec, "%s:%s", branch->name, branch->merge[0]->src); + add_refspec(refspec.buf); +} + +static void setup_default_push_refspecs(void) +{ + git_config(git_default_config, NULL); + switch (push_default) { + case PUSH_DEFAULT_UNSPECIFIED: + /* fallthrough */ + + case PUSH_DEFAULT_MATCHING: + add_refspec(":"); + break; + + case PUSH_DEFAULT_TRACKING: + setup_push_tracking(); + break; + + case PUSH_DEFAULT_CURRENT: + add_refspec("HEAD"); + break; + + case PUSH_DEFAULT_NOTHING: + die("You didn't specify any refspecs to push, and " + "push.default is \"nothing\"."); + break; + } +} + static int do_push(const char *repo, int flags) { int i, errs; @@ -76,11 +118,12 @@ static int do_push(const char *repo, int flags) return error("--all and --mirror are incompatible"); } - if (!refspec - && !(flags & TRANSPORT_PUSH_ALL) - && remote->push_refspec_nr) { - refspec = remote->push_refspec; - refspec_nr = remote->push_refspec_nr; + if (!refspec && !(flags & TRANSPORT_PUSH_ALL)) { + if (remote->push_refspec_nr) { + refspec = remote->push_refspec; + refspec_nr = remote->push_refspec_nr; + } else if (!(flags & TRANSPORT_PUSH_MIRROR)) + setup_default_push_refspecs(); } errs = 0; for (i = 0; i < remote->url_nr; i++) { diff --git a/cache.h b/cache.h index 189151de25..df4f117ac6 100644 --- a/cache.h +++ b/cache.h @@ -541,8 +541,17 @@ enum rebase_setup_type { AUTOREBASE_ALWAYS, }; +enum push_default_type { + PUSH_DEFAULT_UNSPECIFIED = -1, + PUSH_DEFAULT_NOTHING = 0, + PUSH_DEFAULT_MATCHING, + PUSH_DEFAULT_TRACKING, + PUSH_DEFAULT_CURRENT, +}; + extern enum branch_track git_branch_track; extern enum rebase_setup_type autorebase; +extern enum push_default_type push_default; #define GIT_REPO_VERSION 0 extern int repository_format_version; diff --git a/config.c b/config.c index 0c8c76f13b..30443e3279 100644 --- a/config.c +++ b/config.c @@ -565,6 +565,31 @@ static int git_default_branch_config(const char *var, const char *value) return 0; } +static int git_default_push_config(const char *var, const char *value) +{ + if (!strcmp(var, "push.default")) { + if (!value) + return config_error_nonbool(var); + else if (!strcmp(value, "nothing")) + push_default = PUSH_DEFAULT_NOTHING; + else if (!strcmp(value, "matching")) + push_default = PUSH_DEFAULT_MATCHING; + else if (!strcmp(value, "tracking")) + push_default = PUSH_DEFAULT_TRACKING; + else if (!strcmp(value, "current")) + push_default = PUSH_DEFAULT_CURRENT; + else { + error("Malformed value for %s: %s", var, value); + return error("Must be one of nothing, matching, " + "tracking or current."); + } + return 0; + } + + /* Add other config variables here and to Documentation/config.txt. */ + return 0; +} + static int git_default_mailmap_config(const char *var, const char *value) { if (!strcmp(var, "mailmap.file")) @@ -588,6 +613,9 @@ int git_default_config(const char *var, const char *value, void *dummy) if (!prefixcmp(var, "branch.")) return git_default_branch_config(var, value); + if (!prefixcmp(var, "push.")) + return git_default_push_config(var, value); + if (!prefixcmp(var, "mailmap.")) return git_default_mailmap_config(var, value); diff --git a/environment.c b/environment.c index e278bce0ea..4696885b22 100644 --- a/environment.c +++ b/environment.c @@ -42,6 +42,7 @@ enum safe_crlf safe_crlf = SAFE_CRLF_WARN; unsigned whitespace_rule_cfg = WS_DEFAULT_RULE; enum branch_track git_branch_track = BRANCH_TRACK_REMOTE; enum rebase_setup_type autorebase = AUTOREBASE_NEVER; +enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED; /* Parallel index stat data preload? */ int core_preload_index = 0; From 665d3e8f057b90d7b43d70db75b9ac5506bd204d Mon Sep 17 00:00:00 2001 From: Finn Arne Gangstad Date: Mon, 16 Mar 2009 16:42:52 +0100 Subject: [PATCH 109/289] Display warning for default git push with no push.default config If a git push without any refspecs is attempted, display a warning. The current default behavior is to push all matching refspecs, which may come as a surprise to new users, so the warning shows how push.default can be configured and what the possible values are. Traditionalists who wish to keep the current behaviour are also told how to configure this once and never see the warning again. Signed-off-by: Finn Arne Gangstad Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.6.3.txt | 7 +++++++ builtin-push.c | 23 +++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/Documentation/RelNotes-1.6.3.txt b/Documentation/RelNotes-1.6.3.txt index ee1fddb76a..7c02b2a305 100644 --- a/Documentation/RelNotes-1.6.3.txt +++ b/Documentation/RelNotes-1.6.3.txt @@ -22,6 +22,13 @@ branch pointed at by its HEAD, gets a large warning. You can choose what should happen upon such a push by setting the configuration variable receive.denyDeleteCurrent in the receiving repository. +In a future release, the default of "git push" without further +arguments might be changed. Currently, it will push all matching +refspecs to the current remote. A configuration variable push.default +has been introduced to select the default behaviour. To ease the +transition, a big warning is issued if this is not configured and a +git push without arguments is attempted. + Updates since v1.6.2 -------------------- diff --git a/builtin-push.c b/builtin-push.c index 45fe843b20..8f4fa5b09e 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -64,11 +64,34 @@ static void setup_push_tracking(void) add_refspec(refspec.buf); } +static const char *warn_unconfigured_push_msg[] = { + "You did not specify any refspecs to push, and the current remote", + "has not configured any push refspecs. The default action in this", + "case is to push all matching refspecs, that is, all branches", + "that exist both locally and remotely will be updated. This may", + "not necessarily be what you want to happen.", + "", + "You can specify what action you want to take in this case, and", + "avoid seeing this message again, by configuring 'push.default' to:", + " 'nothing' : Do not push anythig", + " 'matching' : Push all matching branches (default)", + " 'tracking' : Push the current branch to whatever it is tracking", + " 'current' : Push the current branch" +}; + +static void warn_unconfigured_push(void) +{ + int i; + for (i = 0; i < ARRAY_SIZE(warn_unconfigured_push_msg); i++) + warning("%s", warn_unconfigured_push_msg[i]); +} + static void setup_default_push_refspecs(void) { git_config(git_default_config, NULL); switch (push_default) { case PUSH_DEFAULT_UNSPECIFIED: + warn_unconfigured_push(); /* fallthrough */ case PUSH_DEFAULT_MATCHING: From 50fd6997c60f926efe4b3591f6c0a37568d02185 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Mon, 16 Mar 2009 19:38:42 +0100 Subject: [PATCH 110/289] pickaxe: count regex matches only once When --pickaxe-regex is used, forward past the end of matches instead of advancing to the byte after their start. This way matches count only once, even if the regular expression matches their tail -- like in the fixed-string fork of the code. E.g.: /.*/ used to count the number of bytes instead of the number of lines. /aa/ resulted in a count of two in "aaa" instead of one. Also document the fact that regexec() needs a NUL-terminated string as its second argument by adding an assert(). Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- diffcore-pickaxe.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c index 574b3e8337..d0ef839700 100644 --- a/diffcore-pickaxe.c +++ b/diffcore-pickaxe.c @@ -25,10 +25,12 @@ static unsigned int contains(struct diff_filespec *one, regmatch_t regmatch; int flags = 0; + assert(data[sz] == '\0'); while (*data && !regexec(regexp, data, 1, ®match, flags)) { flags |= REG_NOTBOL; - data += regmatch.rm_so; - if (*data) data++; + data += regmatch.rm_eo; + if (*data && regmatch.rm_so == regmatch.rm_eo) + data++; cnt++; } From fcfdf797db015e13623645c0be049db679d5daaa Mon Sep 17 00:00:00 2001 From: Michael J Gruber Date: Tue, 17 Mar 2009 15:06:20 +0100 Subject: [PATCH 111/289] git-branch.txt: document -f correctly 'git branch -f a b' resets a to b when a exists, rather then deleting a. Say so in the documentation. Signed-off-by: Michael J Gruber Signed-off-by: Junio C Hamano --- Documentation/git-branch.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index 6103d62fe3..27b73bcf9e 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -76,8 +76,8 @@ OPTIONS based sha1 expressions such as "@\{yesterday}". -f:: - Force the creation of a new branch even if it means deleting - a branch that already exists with the same name. + Reset to if exists + already. Without `-f` 'git-branch' refuses to change an existing branch. -m:: Move/rename a branch and the corresponding reflog. From 8ad3dae3a76ee42983da89ea9b5d9a4688f58f93 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 17 Mar 2009 05:03:19 -0400 Subject: [PATCH 112/289] ls-files: require worktree when --deleted is given The code will end up calling lstat() to check whether the file still exists; obviously this doesn't work if we're not in the worktree. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin-ls-files.c | 1 + 1 file changed, 1 insertion(+) diff --git a/builtin-ls-files.c b/builtin-ls-files.c index 9dec282fba..ca6f33d046 100644 --- a/builtin-ls-files.c +++ b/builtin-ls-files.c @@ -419,6 +419,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix) } if (!strcmp(arg, "-d") || !strcmp(arg, "--deleted")) { show_deleted = 1; + require_work_tree = 1; continue; } if (!strcmp(arg, "-m") || !strcmp(arg, "--modified")) { From 642d0844b9795ca7b1424f4afc8d8c86abd7bf34 Mon Sep 17 00:00:00 2001 From: Emil Sit Date: Tue, 17 Mar 2009 13:31:42 -0400 Subject: [PATCH 113/289] config.txt: Describe special 'none' handling in core.gitProxy. Signed-off-by: Emil Sit Signed-off-by: Junio C Hamano --- Documentation/config.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/config.txt b/Documentation/config.txt index 70fd172c6b..44916b9e7f 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -221,6 +221,11 @@ core.gitProxy:: Can be overridden by the 'GIT_PROXY_COMMAND' environment variable (which always applies universally, without the special "for" handling). ++ +The special string `none` can be used as the proxy command to +specify that no proxy be used for a given domain pattern. +This is useful for excluding servers inside a firewall from +proxy use, while defaulting to a common proxy for external domains. core.ignoreStat:: If true, commands which modify both the working tree and the index From 6e89ec0f1e716c11bf7fa214b4c7b975ea77c736 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 16 Mar 2009 13:20:04 +1100 Subject: [PATCH 114/289] grep: prefer builtin over external one when coloring results MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As far as I know, not all grep programs support coloring, so we should rely on builtin grep. If you want external grep, set color.grep.external to empty string. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin-grep.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/builtin-grep.c b/builtin-grep.c index 9e7e766a49..89489ddcf8 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -825,6 +825,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix) } } + if (opt.color && !opt.color_external) + builtin_grep = 1; if (!opt.pattern_list) die("no pattern given."); if ((opt.regflags != REG_NEWLINE) && opt.fixed) From e986ceb05a118944d2638fba4cd09678c1afa6b3 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 17 Mar 2009 20:26:24 -0700 Subject: [PATCH 115/289] Update draft release notes to 1.6.3 Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.6.3.txt | 37 ++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/Documentation/RelNotes-1.6.3.txt b/Documentation/RelNotes-1.6.3.txt index 679ad28b9d..4353cbf8c3 100644 --- a/Documentation/RelNotes-1.6.3.txt +++ b/Documentation/RelNotes-1.6.3.txt @@ -30,8 +30,18 @@ Updates since v1.6.2 (performance) +* many uses of lstat(2) in the codepath for "git checkout" have been + optimized out. + (usability, bells and whistles) +* rsync:/path/to/repo can be used to run git over rsync for local + repositories. It may not be useful in practice; meant primarily for + testing. + +* (msysgit) progress output that is sent over the sideband protocol can + be handled appropriately in Windows console. + * "--pretty=