From 5ee2ad654bc7d19ec4f08e11dc4fed8b97a59222 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 10 Feb 2006 20:31:09 -0800 Subject: [PATCH 01/11] Make "git clone" less of a deathly quiet experience It used to be that "git-unpack-objects" would give nice percentages, but now that we don't unpack the initial clone pack any more, it doesn't. And I'd love to do that nice percentage view in the pack objects downloader too, but the thing doesn't even read the pack header, much less know how much it's going to get, so I was lazy and didn't. Instead, it at least prints out how much data it's gotten, and what the packing speed is. Which makes the user realize that it's actually doing something useful instead of sitting there silently (and if the recipient knows how large the final result is, he can at least make a guess about when it migt be done). So with this patch, I get something like this on my DSL line: [torvalds@g5 ~]$ time git clone master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 clone-test Packing 188543 objects 48.398MB (154 kB/s) where even the speed approximation seems to be roughtly correct (even though my algorithm is a truly stupid one, and only really gives "speed in the last half second or so"). Anyway, _something_ like this is definitely needed. It could certainly be better (if it showed the same kind of thing that git-unpack-objects did, that would be much nicer, but would require parsing the object stream as it comes in). But this is big step forward, I think. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- cache.h | 2 +- clone-pack.c | 10 ++++++++-- fetch-clone.c | 28 +++++++++++++++++++++++++++- fetch-pack.c | 2 +- 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/cache.h b/cache.h index bdbe2d683e..c255421ab9 100644 --- a/cache.h +++ b/cache.h @@ -348,6 +348,6 @@ extern int copy_fd(int ifd, int ofd); /* Finish off pack transfer receiving end */ extern int receive_unpack_pack(int fd[2], const char *me, int quiet); -extern int receive_keep_pack(int fd[2], const char *me); +extern int receive_keep_pack(int fd[2], const char *me, int quiet); #endif /* CACHE_H */ diff --git a/clone-pack.c b/clone-pack.c index f634431be1..719e1c4fc8 100644 --- a/clone-pack.c +++ b/clone-pack.c @@ -6,6 +6,8 @@ static const char clone_pack_usage[] = "git-clone-pack [--exec=] [:] []*"; static const char *exec = "git-upload-pack"; +static int quiet = 0; + static void clone_handshake(int fd[2], struct ref *ref) { unsigned char sha1[20]; @@ -123,7 +125,9 @@ static int clone_pack(int fd[2], int nr_match, char **match) } clone_handshake(fd, refs); - status = receive_keep_pack(fd, "git-clone-pack"); + if (!quiet) + fprintf(stderr, "Generating pack ...\r"); + status = receive_keep_pack(fd, "git-clone-pack", quiet); if (!status) { if (nr_match == 0) @@ -154,8 +158,10 @@ int main(int argc, char **argv) char *arg = argv[i]; if (*arg == '-') { - if (!strcmp("-q", arg)) + if (!strcmp("-q", arg)) { + quiet = 1; continue; + } if (!strncmp("--exec=", arg, 7)) { exec = arg + 7; continue; diff --git a/fetch-clone.c b/fetch-clone.c index 859f400941..b67d976497 100644 --- a/fetch-clone.c +++ b/fetch-clone.c @@ -1,6 +1,7 @@ #include "cache.h" #include "exec_cmd.h" #include +#include static int finish_pack(const char *pack_tmp_name, const char *me) { @@ -129,10 +130,12 @@ int receive_unpack_pack(int fd[2], const char *me, int quiet) die("git-unpack-objects died of unnatural causes %d", status); } -int receive_keep_pack(int fd[2], const char *me) +int receive_keep_pack(int fd[2], const char *me, int quiet) { char tmpfile[PATH_MAX]; int ofd, ifd; + unsigned long total; + static struct timeval prev_tv; ifd = fd[0]; snprintf(tmpfile, sizeof(tmpfile), @@ -141,6 +144,8 @@ int receive_keep_pack(int fd[2], const char *me) if (ofd < 0) return error("unable to create temporary file %s", tmpfile); + gettimeofday(&prev_tv, NULL); + total = 0; while (1) { char buf[8192]; ssize_t sz, wsz, pos; @@ -165,6 +170,27 @@ int receive_keep_pack(int fd[2], const char *me) } pos += wsz; } + total += sz; + if (!quiet) { + static unsigned long last; + struct timeval tv; + unsigned long diff = total - last; + /* not really "msecs", but a power-of-two millisec (1/1024th of a sec) */ + unsigned long msecs; + + gettimeofday(&tv, NULL); + msecs = tv.tv_sec - prev_tv.tv_sec; + msecs <<= 10; + msecs += (int)(tv.tv_usec - prev_tv.tv_usec) >> 10; + if (msecs > 500) { + prev_tv = tv; + last = total; + fprintf(stderr, "%4lu.%03luMB (%lu kB/s) \r", + total >> 20, + 1000*((total >> 10) & 1023)>>10, + diff / msecs ); + } + } } close(ofd); return finish_pack(tmpfile, me); diff --git a/fetch-pack.c b/fetch-pack.c index 27f5d2a5ff..aa6f42ae1b 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -378,7 +378,7 @@ static int fetch_pack(int fd[2], int nr_match, char **match) fprintf(stderr, "warning: no common commits\n"); if (keep_pack) - status = receive_keep_pack(fd, "git-fetch-pack"); + status = receive_keep_pack(fd, "git-fetch-pack", quiet); else status = receive_unpack_pack(fd, "git-fetch-pack", quiet); From c548cf4ee0737a321ffe94f6a97c65baf87281be Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 11 Feb 2006 10:43:56 -0800 Subject: [PATCH 02/11] Make "git clone" pack-fetching download statistics better Average it out over a few events to make the numbers stable, and fix the silly usec->binary-ms conversion. Yeah, yeah, it's arguably eye-candy to keep the user calm, but let's do that right. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- fetch-clone.c | 44 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/fetch-clone.c b/fetch-clone.c index b67d976497..873312df3d 100644 --- a/fetch-clone.c +++ b/fetch-clone.c @@ -130,12 +130,35 @@ int receive_unpack_pack(int fd[2], const char *me, int quiet) die("git-unpack-objects died of unnatural causes %d", status); } +/* + * We average out the download speed over this many "events", where + * an event is a minimum of about half a second. That way, we get + * a reasonably stable number. + */ +#define NR_AVERAGE (4) + +/* + * A "binary msec" is a power-of-two-msec, aka 1/1024th of a second. + * Keeing the time in that format means that "bytes / msecs" means + * is the same as kB/s (modulo rounding). + * + * 1000512 is a magic number (usecs in a second, rounded up by half + * of 1024, to make "rounding" come out right ;) + */ +#define usec_to_binarymsec(x) ((int)(x) / (1000512 >> 10)) + int receive_keep_pack(int fd[2], const char *me, int quiet) { char tmpfile[PATH_MAX]; int ofd, ifd; unsigned long total; static struct timeval prev_tv; + struct average { + unsigned long bytes; + unsigned long time; + } download[NR_AVERAGE] = { {0, 0}, }; + unsigned long avg_bytes, avg_time; + int idx = 0; ifd = fd[0]; snprintf(tmpfile, sizeof(tmpfile), @@ -146,6 +169,8 @@ int receive_keep_pack(int fd[2], const char *me, int quiet) gettimeofday(&prev_tv, NULL); total = 0; + avg_bytes = 0; + avg_time = 0; while (1) { char buf[8192]; ssize_t sz, wsz, pos; @@ -181,14 +206,27 @@ int receive_keep_pack(int fd[2], const char *me, int quiet) gettimeofday(&tv, NULL); msecs = tv.tv_sec - prev_tv.tv_sec; msecs <<= 10; - msecs += (int)(tv.tv_usec - prev_tv.tv_usec) >> 10; + msecs += usec_to_binarymsec(tv.tv_usec - prev_tv.tv_usec); + if (msecs > 500) { prev_tv = tv; last = total; - fprintf(stderr, "%4lu.%03luMB (%lu kB/s) \r", + + /* Update averages ..*/ + avg_bytes += diff; + avg_time += msecs; + avg_bytes -= download[idx].bytes; + avg_time -= download[idx].time; + download[idx].bytes = diff; + download[idx].time = msecs; + idx++; + if (idx >= NR_AVERAGE) + idx = 0; + + fprintf(stderr, "%4lu.%03luMB (%lu kB/s) \r", total >> 20, 1000*((total >> 10) & 1023)>>10, - diff / msecs ); + avg_bytes / avg_time ); } } } From 98deeaa82fb2b96395854e0574272ed21cbd81fe Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 11 Feb 2006 10:41:22 -0800 Subject: [PATCH 03/11] Fix fetch-clone in the presense of signals We shouldn't fail a fetch just because a signal might have interrupted the read. Normally, we don't install any signal handlers, so EINTR really shouldn't happen. That said, really old versions of Linux will interrupt an interruptible system call even for signals that turn out to be ignored (SIGWINCH is the classic example - resizing your xterm would cause it). The same might well be true elsewhere too. Also, since receive_keep_pack() doesn't control the caller, it can't know that no signal handlers exist. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- fetch-clone.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/fetch-clone.c b/fetch-clone.c index 873312df3d..da1b3ffbaa 100644 --- a/fetch-clone.c +++ b/fetch-clone.c @@ -178,10 +178,13 @@ int receive_keep_pack(int fd[2], const char *me, int quiet) if (sz == 0) break; if (sz < 0) { - error("error reading pack (%s)", strerror(errno)); - close(ofd); - unlink(tmpfile); - return -1; + if (errno != EINTR && errno != EAGAIN) { + error("error reading pack (%s)", strerror(errno)); + close(ofd); + unlink(tmpfile); + return -1; + } + sz = 0; } pos = 0; while (pos < sz) { From 21fcd1bdea2440236aea1713ea42a66bc2da5563 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 11 Feb 2006 17:54:18 -0800 Subject: [PATCH 04/11] fetch-clone progress: finishing touches. This makes fetch-pack also report the progress of packing part. Signed-off-by: Junio C Hamano --- clone-pack.c | 4 ++-- pack-objects.c | 43 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/clone-pack.c b/clone-pack.c index 719e1c4fc8..a4370f595f 100644 --- a/clone-pack.c +++ b/clone-pack.c @@ -125,9 +125,9 @@ static int clone_pack(int fd[2], int nr_match, char **match) } clone_handshake(fd, refs); - if (!quiet) - fprintf(stderr, "Generating pack ...\r"); status = receive_keep_pack(fd, "git-clone-pack", quiet); + if (!quiet) + fprintf(stderr, "\n"); if (!status) { if (nr_match == 0) diff --git a/pack-objects.c b/pack-objects.c index c3f25317bb..2135e9a92e 100644 --- a/pack-objects.c +++ b/pack-objects.c @@ -3,6 +3,7 @@ #include "delta.h" #include "pack.h" #include "csum-file.h" +#include static const char pack_usage[] = "git-pack-objects [--non-empty] [--local] [--incremental] [--window=N] [--depth=N] {--stdout | base-name} < object-list"; @@ -26,6 +27,7 @@ static struct object_entry *objects = NULL; static int nr_objects = 0, nr_alloc = 0; static const char *base_name; static unsigned char pack_file_sha1[20]; +static int progress = 0; static void *delta_against(void *buf, unsigned long size, struct object_entry *entry) { @@ -362,10 +364,13 @@ static void find_deltas(struct object_entry **list, int window, int depth) int i, idx; unsigned int array_size = window * sizeof(struct unpacked); struct unpacked *array = xmalloc(array_size); + int eye_candy; memset(array, 0, array_size); i = nr_objects; idx = 0; + eye_candy = i - (nr_objects / 20); + while (--i >= 0) { struct object_entry *entry = list[i]; struct unpacked *n = array + idx; @@ -373,6 +378,10 @@ static void find_deltas(struct object_entry **list, int window, int depth) char type[10]; int j; + if (progress && i <= eye_candy) { + eye_candy -= nr_objects / 20; + fputc('.', stderr); + } free(n->data); n->entry = entry; n->data = read_sha1_file(entry->sha1, type, &size); @@ -404,11 +413,13 @@ static void prepare_pack(int window, int depth) { get_object_details(); - fprintf(stderr, "Packing %d objects\n", nr_objects); - + if (progress) + fprintf(stderr, "Packing %d objects", nr_objects); sorted_by_type = create_sorted_list(type_size_sort); if (window && depth) find_deltas(sorted_by_type, window+1, depth); + if (progress) + fputc('\n', stderr); write_pack_file(); } @@ -472,6 +483,10 @@ int main(int argc, char **argv) int window = 10, depth = 10, pack_to_stdout = 0; struct object_entry **list; int i; + struct timeval prev_tv; + int eye_candy = 0; + int eye_candy_incr = 500; + setup_git_directory(); @@ -519,12 +534,34 @@ int main(int argc, char **argv) if (pack_to_stdout != !base_name) usage(pack_usage); + progress = isatty(2); + prepare_packed_git(); + if (progress) { + fprintf(stderr, "Generating pack...\n"); + gettimeofday(&prev_tv, NULL); + } while (fgets(line, sizeof(line), stdin) != NULL) { unsigned int hash; char *p; unsigned char sha1[20]; + if (progress && (eye_candy <= nr_objects)) { + fprintf(stderr, "Counting objects...%d\r", nr_objects); + if (eye_candy && (50 <= eye_candy_incr)) { + struct timeval tv; + int time_diff; + gettimeofday(&tv, NULL); + time_diff = (tv.tv_sec - prev_tv.tv_sec); + time_diff <<= 10; + time_diff += (tv.tv_usec - prev_tv.tv_usec); + if ((1 << 9) < time_diff) + eye_candy_incr += 50; + else if (50 < eye_candy_incr) + eye_candy_incr -= 50; + } + eye_candy += eye_candy_incr; + } if (get_sha1_hex(line, sha1)) die("expected sha1, got garbage:\n %s", line); hash = 0; @@ -537,6 +574,8 @@ int main(int argc, char **argv) } add_object_entry(sha1, hash); } + if (progress) + fprintf(stderr, "Done counting %d objects.\n", nr_objects); if (non_empty && !nr_objects) return 0; From 7bbdeaa969794edd67cd4a1ed09b9a057b6af4c3 Mon Sep 17 00:00:00 2001 From: Christian Biesinger Date: Sat, 11 Feb 2006 16:44:11 +0100 Subject: [PATCH 05/11] Use a relative path for SVN importing The absolute path (with the leading slash) breaks SVN importing, because it then looks for /trunk/... instead of /svn/trunk/... (in my case, the repository URL was https://servername/svn/) Signed-off-by: Christian Biesinger Signed-off-by: Junio C Hamano --- git-svnimport.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-svnimport.perl b/git-svnimport.perl index b6799d81ee..f17d5a27c8 100755 --- a/git-svnimport.perl +++ b/git-svnimport.perl @@ -318,7 +318,7 @@ sub get_file($$$) { die $res->status_line." at $url\n"; } } else { - $name = $svn->file("/$svnpath",$rev); + $name = $svn->file("$svnpath",$rev); return undef unless defined $name; } From 1536dd9c61b5582cf079999057cb715dd6dc6620 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 11 Feb 2006 18:55:43 -0800 Subject: [PATCH 06/11] Only call git-rerere if $GIT_DIR/rr-cache exists. Johannes noticed that git-rerere depends on Digest.pm, and if one does not use the command, one can live without it. Signed-off-by: Junio C Hamano --- git-am.sh | 5 ++++- git-commit.sh | 5 ++++- git-merge.sh | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/git-am.sh b/git-am.sh index ee6886f300..98b9215f70 100755 --- a/git-am.sh +++ b/git-am.sh @@ -88,7 +88,10 @@ fall_back_3way () { # saying that we reverted all those changes. git-merge-resolve $orig_tree -- HEAD $his_tree || { - git-rerere + if test -d "$GIT_DIR/rr-cache" + then + git-rerere + fi echo Failed to merge in the changes. exit 1 } diff --git a/git-commit.sh b/git-commit.sh index 073ec81e14..59551d99f9 100755 --- a/git-commit.sh +++ b/git-commit.sh @@ -638,7 +638,10 @@ else fi ret="$?" rm -f "$GIT_DIR/COMMIT_MSG" "$GIT_DIR/COMMIT_EDITMSG" -git-rerere +if test -d "$GIT_DIR/rr-cache" +then + git-rerere +fi if test -x "$GIT_DIR"/hooks/post-commit && test "$ret" = 0 then diff --git a/git-merge.sh b/git-merge.sh index dc17baf6e0..74f07610fa 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -309,6 +309,9 @@ Conflicts: sed -e 's/^[^ ]* / /' | uniq } >>"$GIT_DIR/MERGE_MSG" - git rerere + if test -d "$GIT_DIR/rr-cache" + then + git-rerere + fi die "Automatic merge failed; fix up by hand" fi From 16139f9035137ccd81e1e9a9dc203fbede6997a0 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 11 Feb 2006 23:08:23 -0800 Subject: [PATCH 07/11] t5500: adjust to change in pack-object reporting behaviour. Now pack-object is not as chatty when its stderr is not connected to a terminal, so the test needs to be adjusted for that. Signed-off-by: Junio C Hamano --- t/t5500-fetch-pack.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index 0781bd287e..e15e14fc32 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -76,7 +76,7 @@ function pull_to_client () { git-symbolic-ref HEAD refs/heads/${heads:0:1} test_expect_success "fsck" 'git-fsck-objects --full > fsck.txt 2>&1' test_expect_object_count "after $number pull" $count - pack_count=$(grep Packing log.txt|tr -dc "0-9") + pack_count=$(grep Unpacking log.txt|tr -dc "0-9") test -z "$pack_count" && pack_count=0 if [ -z "$no_strict_count_check" ]; then test_expect_success "minimal count" "test $count = $pack_count" From 6932c78cb4485380a8ff63575a47a58a4e308bfd Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 11 Feb 2006 16:43:30 -0800 Subject: [PATCH 08/11] diff-tree: do not default to -c Marco says it breaks qgit. This makes the flags a bit more orthogonal. $ git-diff-tree -r --abbrev ca18 No output from this command because you asked to skip merge by not having -m there. $ git-diff-tree -r -m --abbrev ca18 ca182053c7710a286d72102f4576cf32e0dafcfb :100644 100644 538d21d... 59042d1... M Makefile :100644 100644 410b758... 6c47c3a... M entry.c ca182053c7710a286d72102f4576cf32e0dafcfb :100644 100644 30479b4... 59042d1... M Makefile The same "independent sets of diff" as before without -c. $ git-diff-tree -r -m -c --abbrev ca18 ca182053c7710a286d72102f4576cf32e0dafcfb ::100644 100644 100644 538d21d... 30479b4... 59042d1... MM Makefile Combined. $ git-diff-tree -r -c --abbrev ca18 ca182053c7710a286d72102f4576cf32e0dafcfb ::100644 100644 100644 538d21d... 30479b4... 59042d1... MM Makefile Asking for combined without -m does not make sense, so -c implies -m. We need to supply -c as default to whatchanged, which is a one-liner. Signed-off-by: Junio C Hamano --- diff-tree.c | 4 ++-- git-whatchanged.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/diff-tree.c b/diff-tree.c index b170b03fd3..f55a35a9d5 100644 --- a/diff-tree.c +++ b/diff-tree.c @@ -6,7 +6,7 @@ static int show_root_diff = 0; static int no_commit_id = 0; static int verbose_header = 0; static int ignore_merges = 1; -static int combine_merges = 1; +static int combine_merges = 0; static int dense_combined_merges = 0; static int read_stdin = 0; static int always_show_header = 0; @@ -248,7 +248,7 @@ int main(int argc, const char **argv) continue; } if (!strcmp(arg, "-m")) { - combine_merges = ignore_merges = 0; + ignore_merges = 0; continue; } if (!strcmp(arg, "-c")) { diff --git a/git-whatchanged.sh b/git-whatchanged.sh index 574fc3558e..1fb9feb348 100755 --- a/git-whatchanged.sh +++ b/git-whatchanged.sh @@ -10,7 +10,7 @@ case "$0" in count= test -z "$diff_tree_flags" && diff_tree_flags=$(git-repo-config --get whatchanged.difftree) - diff_tree_default_flags='-M --abbrev' ;; + diff_tree_default_flags='-c -M --abbrev' ;; *show) count=-n1 test -z "$diff_tree_flags" && From 7162dff3dde6fb0a2829fbc8e641fc6d1e7e76ec Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Sun, 12 Feb 2006 04:14:48 +0100 Subject: [PATCH 09/11] Add support for explicit type specifiers when calling git-repo-config Currently, git-repo-config will just return the raw value of option as specified in the config file; this makes things difficult for scripts calling it, especially if the value is supposed to be boolean. This patch makes it possible to ask git-repo-config to check if the option is of the given type (int or bool) and write out the value in its canonical form. If you do not pass --int or --bool, the behaviour stays unchanged and the raw value is emitted. This also incidentally fixes the segfault when option with no value is encountered. [jc: tweaked the option parsing a bit to make it easier to see that the patch does not change anything but the type stuff in the diff output. Also changed to avoid "foo ? : bar" construct. ] Signed-off-by: Petr Baudis Signed-off-by: Junio C Hamano --- Documentation/git-repo-config.txt | 18 ++++++++++++------ repo-config.c | 27 +++++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/Documentation/git-repo-config.txt b/Documentation/git-repo-config.txt index 306946412f..33fcde452a 100644 --- a/Documentation/git-repo-config.txt +++ b/Documentation/git-repo-config.txt @@ -8,12 +8,12 @@ git-repo-config - Get and set options in .git/config. SYNOPSIS -------- -'git-repo-config' name [value [value_regex]] -'git-repo-config' --replace-all name [value [value_regex]] -'git-repo-config' --get name [value_regex] -'git-repo-config' --get-all name [value_regex] -'git-repo-config' --unset name [value_regex] -'git-repo-config' --unset-all name [value_regex] +'git-repo-config' [type] name [value [value_regex]] +'git-repo-config' [type] --replace-all name [value [value_regex]] +'git-repo-config' [type] --get name [value_regex] +'git-repo-config' [type] --get-all name [value_regex] +'git-repo-config' [type] --unset name [value_regex] +'git-repo-config' [type] --unset-all name [value_regex] DESCRIPTION ----------- @@ -26,6 +26,12 @@ should provide a POSIX regex for the value. If you want to handle the lines *not* matching the regex, just prepend a single exclamation mark in front (see EXAMPLES). +The type specifier can be either '--int' or '--bool', which will make +'git-repo-config' ensure that the variable(s) are of the given type and +convert the value to the canonical form (simple decimal number for int, +a "true" or "false" string for bool). If no type specifier is passed, +no checks or transformations are performed on the value. + This command will fail if . .git/config is invalid, diff --git a/repo-config.c b/repo-config.c index c31e441a3d..9cf65193f9 100644 --- a/repo-config.c +++ b/repo-config.c @@ -2,7 +2,7 @@ #include static const char git_config_set_usage[] = -"git-repo-config [--get | --get-all | --replace-all | --unset | --unset-all] name [value [value_regex]]"; +"git-repo-config [ --bool | --int ] [--get | --get-all | --replace-all | --unset | --unset-all] name [value [value_regex]]"; static char* key = NULL; static char* value = NULL; @@ -10,6 +10,7 @@ static regex_t* regexp = NULL; static int do_all = 0; static int do_not_match = 0; static int seen = 0; +static enum { T_RAW, T_INT, T_BOOL } type = T_RAW; static int show_config(const char* key_, const char* value_) { @@ -25,7 +26,17 @@ static int show_config(const char* key_, const char* value_) fprintf(stderr, "More than one value: %s\n", value); free(value); } - value = strdup(value_); + + if (type == T_INT) { + value = malloc(256); + sprintf(value, "%d", git_config_int(key_, value_)); + } else if (type == T_BOOL) { + value = malloc(256); + sprintf(value, "%s", git_config_bool(key_, value_) + ? "true" : "false"); + } else { + value = strdup(value_ ? value_ : ""); + } seen++; } return 0; @@ -73,6 +84,18 @@ static int get_value(const char* key_, const char* regex_) int main(int argc, const char **argv) { setup_git_directory(); + + while (1 < argc) { + if (!strcmp(argv[1], "--int")) + type = T_INT; + else if (!strcmp(argv[1], "--bool")) + type = T_BOOL; + else + break; + argc--; + argv++; + } + switch (argc) { case 2: return get_value(argv[1], NULL); From 4890f62bc02929b174ff5fa0e3656ea3d40f0e57 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 11 Feb 2006 12:39:11 -0800 Subject: [PATCH 10/11] Avoid using "git-var -l" until it gets fixed. This is to be nicer to people with unusable GECOS field. "git-var -l" is currently broken in that when used by a user who does not have a usable GECOS field and has not corrected it by exporting GIT_COMMITTER_NAME environment variable it dies when it tries to output GIT_COMMITTER_IDENT (same thing for AUTHOR). "git-pull" used "git-var -l" only because it needed to get a configuration variable before "git-repo-config --get" was introduced. Use the latter tool designed exactly for this purpose. "git-sh-setup" used "git-var GIT_AUTHOR_IDENT" without actually wanting to use its value. The only purpose was to cause the command to check and barf if the repository format version recorded in the $GIT_DIR/config file is too new for us to deal with correctly. Instead, use "repo-config --get" on a random property and see if it die()s, and check if the exit status is 128 (comes from die -- missing variable is reported with exit status 1, so we can tell that case apart). Signed-off-by: Junio C Hamano --- git-pull.sh | 8 ++++---- git-sh-setup.sh | 6 +++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/git-pull.sh b/git-pull.sh index 0991d5f14c..6caf1aad47 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -70,21 +70,21 @@ case "$merge_head" in exit 0 ;; ?*' '?*) - var=`git-var -l | sed -ne 's/^pull\.octopus=/-s /p'` + var=`git repo-config --get pull.octopus` if test '' = "$var" then strategy_default_args='-s octopus' else - strategy_default_args=$var + strategy_default_args="-s $var" fi ;; *) - var=`git-var -l | sed -ne 's/^pull\.twohead=/-s /p'` + var=`git repo-config --get pull.twohead` if test '' = "$var" then strategy_default_args='-s recursive' else - strategy_default_args=$var + strategy_default_args="-s $var" fi ;; esac diff --git a/git-sh-setup.sh b/git-sh-setup.sh index 1e638e493d..157c7e4d6c 100755 --- a/git-sh-setup.sh +++ b/git-sh-setup.sh @@ -41,7 +41,11 @@ then : ${GIT_OBJECT_DIRECTORY="$GIT_DIR/objects"} # Make sure we are in a valid repository of a vintage we understand. - GIT_DIR="$GIT_DIR" git-var GIT_AUTHOR_IDENT >/dev/null || exit + GIT_DIR="$GIT_DIR" git repo-config --get core.nosuch >/dev/null + if test $? == 128 + then + exit + fi else GIT_DIR=$(git-rev-parse --git-dir) || exit fi From 5b766ea9014b4121cb72f424633b6bf9a97308a0 Mon Sep 17 00:00:00 2001 From: "kent@lysator.liu.se" Date: Sun, 12 Feb 2006 13:00:52 +0100 Subject: [PATCH 11/11] Add howto about separating topics. This howto consists of a footnote from an email by JC to the git mailing list (<7vfyms0x4p.fsf@assigned-by-dhcp.cox.net>). Signed-off-by: Kent Engstrom Signed-off-by: Junio C Hamano --- .../howto/separating-topic-branches.txt | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 Documentation/howto/separating-topic-branches.txt diff --git a/Documentation/howto/separating-topic-branches.txt b/Documentation/howto/separating-topic-branches.txt new file mode 100644 index 0000000000..090e2c9b01 --- /dev/null +++ b/Documentation/howto/separating-topic-branches.txt @@ -0,0 +1,91 @@ +From: Junio C Hamano +Subject: Separating topic branches +Abstract: In this article, JC describes how to separate topic branches. + +This text was originally a footnote to a discussion about the +behaviour of the git diff commands. + +Often I find myself doing that [running diff against something other +than HEAD] while rewriting messy development history. For example, I +start doing some work without knowing exactly where it leads, and end +up with a history like this: + + "master" + o---o + \ "topic" + o---o---o---o---o---o + +At this point, "topic" contains something I know I want, but it +contains two concepts that turned out to be completely independent. +And often, one topic component is larger than the other. It may +contain more than two topics. + +In order to rewrite this mess to be more manageable, I would first do +"diff master..topic", to extract the changes into a single patch, start +picking pieces from it to get logically self-contained units, and +start building on top of "master": + + $ git diff master..topic >P.diff + $ git checkout -b topicA master + ... pick and apply pieces from P.diff to build + ... commits on topicA branch. + + o---o---o + / "topicA" + o---o"master" + \ "topic" + o---o---o---o---o---o + +Before doing each commit on "topicA" HEAD, I run "diff HEAD" +before update-index the affected paths, or "diff --cached HEAD" +after. Also I would run "diff --cached master" to make sure +that the changes are only the ones related to "topicA". Usually +I do this for smaller topics first. + +After that, I'd do the remainder of the original "topic", but +for that, I do not start from the patchfile I extracted by +comparing "master" and "topic" I used initially. Still on +"topicA", I extract "diff topic", and use it to rebuild the +other topic: + + $ git diff -R topic >P.diff ;# --cached also would work fine + $ git checkout -b topicB master + ... pick and apply pieces from P.diff to build + ... commits on topicB branch. + + "topicB" + o---o---o---o---o + / + /o---o---o + |/ "topicA" + o---o"master" + \ "topic" + o---o---o---o---o---o + +After I am done, I'd try a pretend-merge between "topicA" and +"topicB" in order to make sure I have not missed anything: + + $ git pull . topicA ;# merge it into current "topicB" + $ git diff topic + "topicB" + o---o---o---o---o---* (pretend merge) + / / + /o---o---o----------' + |/ "topicA" + o---o"master" + \ "topic" + o---o---o---o---o---o + +The last diff better not to show anything other than cleanups +for crufts. Then I can finally clean things up: + + $ git branch -D topic + $ git reset --hard HEAD^ ;# nuke pretend merge + + "topicB" + o---o---o---o---o + / + /o---o---o + |/ "topicA" + o---o"master" +