From ae5a6c3684c378bc32c1f6ecc0e6dc45300c14c1 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 17 Jan 2009 17:09:53 +0100 Subject: checkout: implement "@{-N}" shortcut name for N-th last branch Implement a shortcut @{-N} for the N-th last branch checked out, that works by parsing the reflog for the message added by previous git-checkout invocations. We expand the @{-N} to the branch name, so that you end up on an attached HEAD on that branch. Signed-off-by: Junio C Hamano diff --git a/builtin-checkout.c b/builtin-checkout.c index b5dd9c0..a3b69d6 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -361,8 +361,14 @@ struct branch_info { static void setup_branch_path(struct branch_info *branch) { struct strbuf buf = STRBUF_INIT; - strbuf_addstr(&buf, "refs/heads/"); - strbuf_addstr(&buf, branch->name); + + if (!interpret_nth_last_branch(branch->name, &buf)) { + branch->name = xstrdup(buf.buf); + strbuf_splice(&buf, 0, 0, "refs/heads/", 11); + } else { + strbuf_addstr(&buf, "refs/heads/"); + strbuf_addstr(&buf, branch->name); + } branch->path = strbuf_detach(&buf, NULL); } diff --git a/cache.h b/cache.h index 8e1af26..0dd9168 100644 --- a/cache.h +++ b/cache.h @@ -663,6 +663,7 @@ extern int read_ref(const char *filename, unsigned char *sha1); extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *); extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref); extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref); +extern int interpret_nth_last_branch(const char *str, struct strbuf *); extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules); extern const char *ref_rev_parse_rules[]; diff --git a/sha1_name.c b/sha1_name.c index 159c2ab8..6377264 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -674,6 +674,84 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1) return retval; } +struct grab_nth_branch_switch_cbdata { + int counting; + int nth; + struct strbuf *buf; +}; + +static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1, + const char *email, unsigned long timestamp, int tz, + const char *message, void *cb_data) +{ + struct grab_nth_branch_switch_cbdata *cb = cb_data; + const char *match = NULL; + + if (!prefixcmp(message, "checkout: moving to ")) + match = message + strlen("checkout: moving to "); + else if (!prefixcmp(message, "checkout: moving from ")) { + const char *cp = message + strlen("checkout: moving from "); + if ((cp = strstr(cp, " to ")) != NULL) { + match = cp + 4; + } + } + + if (!match) + return 0; + + if (cb->counting) { + cb->nth++; + return 0; + } + + if (--cb->nth <= 0) { + size_t len = strlen(match); + while (match[len-1] == '\n') + len--; + strbuf_reset(cb->buf); + strbuf_add(cb->buf, match, len); + return 1; + } + return 0; +} + +/* + * This reads "@{-N}" syntax, finds the name of the Nth previous + * branch we were on, and places the name of the branch in the given + * buf and returns 0 if successful. + * + * If the input is not of the accepted format, it returns a negative + * number to signal an error. + */ +int interpret_nth_last_branch(const char *name, struct strbuf *buf) +{ + int nth, i; + struct grab_nth_branch_switch_cbdata cb; + + if (name[0] != '@' || name[1] != '{' || name[2] != '-') + return -1; + for (i = 3, nth = 0; name[i] && name[i] != '}'; i++) { + char ch = name[i]; + if ('0' <= ch && ch <= '9') + nth = nth * 10 + ch - '0'; + else + return -1; + } + if (nth < 0 || 10 <= nth) + return -1; + + cb.counting = 1; + cb.nth = 0; + cb.buf = buf; + for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb); + + cb.counting = 0; + cb.nth -= nth; + cb.buf = buf; + for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb); + return 0; +} + /* * This is like "get_sha1_basic()", except it allows "sha1 expressions", * notably "xyz^" for "parent of xyz" -- cgit v0.10.2-6-g49f6 From a884d0cb71463c28d0329c593dce1ef9758f6177 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Sat, 17 Jan 2009 17:09:54 +0100 Subject: sha1_name: tweak @{-N} lookup Have the lookup only look at "interesting" checkouts, meaning those that tell you "Already on ..." don't count even though they also cause a reflog entry. Let interpret_nth_last_branch() return the number of characters parsed, so that git-checkout can verify that the branch spec was @{-N}, not @{-1}^2 or something like that. (The latter will be added later.) Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano diff --git a/builtin-checkout.c b/builtin-checkout.c index a3b69d6..dc1de06 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -361,8 +361,10 @@ struct branch_info { static void setup_branch_path(struct branch_info *branch) { struct strbuf buf = STRBUF_INIT; + int ret; - if (!interpret_nth_last_branch(branch->name, &buf)) { + if ((ret = interpret_nth_last_branch(branch->name, &buf)) + && ret == strlen(branch->name)) { branch->name = xstrdup(buf.buf); strbuf_splice(&buf, 0, 0, "refs/heads/", 11); } else { diff --git a/sha1_name.c b/sha1_name.c index 6377264..34e39db 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -685,29 +685,28 @@ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1, const char *message, void *cb_data) { struct grab_nth_branch_switch_cbdata *cb = cb_data; - const char *match = NULL; - - if (!prefixcmp(message, "checkout: moving to ")) - match = message + strlen("checkout: moving to "); - else if (!prefixcmp(message, "checkout: moving from ")) { - const char *cp = message + strlen("checkout: moving from "); - if ((cp = strstr(cp, " to ")) != NULL) { - match = cp + 4; - } + const char *match = NULL, *target = NULL; + size_t len; + + if (!prefixcmp(message, "checkout: moving from ")) { + match = message + strlen("checkout: moving from "); + if ((target = strstr(match, " to ")) != NULL) + target += 4; } if (!match) return 0; + len = target - match - 4; + if (target[len] == '\n' && !strncmp(match, target, len)) + return 0; + if (cb->counting) { cb->nth++; return 0; } - if (--cb->nth <= 0) { - size_t len = strlen(match); - while (match[len-1] == '\n') - len--; + if (cb->nth-- <= 0) { strbuf_reset(cb->buf); strbuf_add(cb->buf, match, len); return 1; @@ -718,26 +717,28 @@ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1, /* * This reads "@{-N}" syntax, finds the name of the Nth previous * branch we were on, and places the name of the branch in the given - * buf and returns 0 if successful. + * buf and returns the number of characters parsed if successful. * * If the input is not of the accepted format, it returns a negative * number to signal an error. + * + * If the input was ok but there are not N branch switches in the + * reflog, it returns 0. */ int interpret_nth_last_branch(const char *name, struct strbuf *buf) { - int nth, i; + int nth; struct grab_nth_branch_switch_cbdata cb; + const char *brace; + char *num_end; if (name[0] != '@' || name[1] != '{' || name[2] != '-') return -1; - for (i = 3, nth = 0; name[i] && name[i] != '}'; i++) { - char ch = name[i]; - if ('0' <= ch && ch <= '9') - nth = nth * 10 + ch - '0'; - else - return -1; - } - if (nth < 0 || 10 <= nth) + brace = strchr(name, '}'); + if (!brace) + return -1; + nth = strtol(name+3, &num_end, 10); + if (num_end != brace) return -1; cb.counting = 1; @@ -745,11 +746,15 @@ int interpret_nth_last_branch(const char *name, struct strbuf *buf) cb.buf = buf; for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb); + if (cb.nth < nth) + return 0; + cb.counting = 0; cb.nth -= nth; cb.buf = buf; for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb); - return 0; + + return brace-name+1; } /* -- cgit v0.10.2-6-g49f6 From d18ba22154574390dbff2c060f44b9715477e95a Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Sat, 17 Jan 2009 17:09:55 +0100 Subject: sha1_name: support @{-N} syntax in get_sha1() Let get_sha1() parse the @{-N} syntax, with docs and tests. Note that while @{-1}^2, @{-2}~5 and such are supported, @{-1}@{1} is currently not allowed. Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index 2921da3..3ccef2f 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -212,6 +212,9 @@ when you run 'git-merge'. reflog of the current branch. For example, if you are on the branch 'blabla', then '@\{1\}' means the same as 'blabla@\{1\}'. +* The special construct '@\{-\}' means the th branch checked out + before the current one. + * A suffix '{caret}' to a revision parameter means the first parent of that commit object. '{caret}' means the th parent (i.e. 'rev{caret}' diff --git a/sha1_name.c b/sha1_name.c index 34e39db..9e1538e 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -297,6 +297,8 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log) return logs_found; } +static int get_sha1_1(const char *name, int len, unsigned char *sha1); + static int get_sha1_basic(const char *str, int len, unsigned char *sha1) { static const char *warning = "warning: refname '%.*s' is ambiguous.\n"; @@ -307,7 +309,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) if (len == 40 && !get_sha1_hex(str, sha1)) return 0; - /* basic@{time or number} format to query ref-log */ + /* basic@{time or number or -number} format to query ref-log */ reflog_len = at = 0; if (str[len-1] == '}') { for (at = 0; at < len - 1; at++) { @@ -324,6 +326,16 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) return -1; if (!len && reflog_len) { + struct strbuf buf = STRBUF_INIT; + int ret; + /* try the @{-N} syntax for n-th checkout */ + ret = interpret_nth_last_branch(str+at, &buf); + if (ret > 0) { + /* substitute this branch name and restart */ + return get_sha1_1(buf.buf, buf.len, sha1); + } else if (ret == 0) { + return -1; + } /* allow "@{...}" to mean the current branch reflog */ refs_found = dwim_ref("HEAD", 4, sha1, &real_ref); } else if (reflog_len) @@ -379,8 +391,6 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) return 0; } -static int get_sha1_1(const char *name, int len, unsigned char *sha1); - static int get_parent(const char *name, int len, unsigned char *result, int idx) { diff --git a/t/t1505-rev-parse-last.sh b/t/t1505-rev-parse-last.sh new file mode 100755 index 0000000..1e49dd2 --- /dev/null +++ b/t/t1505-rev-parse-last.sh @@ -0,0 +1,71 @@ +#!/bin/sh + +test_description='test @{-N} syntax' + +. ./test-lib.sh + + +make_commit () { + echo "$1" > "$1" && + git add "$1" && + git commit -m "$1" +} + + +test_expect_success 'setup' ' + + make_commit 1 && + git branch side && + make_commit 2 && + make_commit 3 && + git checkout side && + make_commit 4 && + git merge master && + git checkout master + +' + +# 1 -- 2 -- 3 master +# \ \ +# \ \ +# --- 4 --- 5 side +# +# and 'side' should be the last branch + +git log --graph --all --pretty=oneline --decorate + +test_rev_equivalent () { + + git rev-parse "$1" > expect && + git rev-parse "$2" > output && + test_cmp expect output + +} + +test_expect_success '@{-1} works' ' + test_rev_equivalent side @{-1} +' + +test_expect_success '@{-1}~2 works' ' + test_rev_equivalent side~2 @{-1}~2 +' + +test_expect_success '@{-1}^2 works' ' + test_rev_equivalent side^2 @{-1}^2 +' + +test_expect_failure '@{-1}@{1} works' ' + test_rev_equivalent side@{1} @{-1}@{1} +' + +test_expect_success '@{-2} works' ' + test_rev_equivalent master @{-2} +' + +test_expect_success '@{-3} fails' ' + test_must_fail git rev-parse @{-3} +' + +test_done + + -- cgit v0.10.2-6-g49f6 From 696acf45f9638b014c7132508de26d1a571c8a33 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Sat, 17 Jan 2009 17:09:56 +0100 Subject: checkout: implement "-" abbreviation, add docs and tests Have '-' mean the same as '@{-1}', i.e., the last branch we were on. Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 9cd5151..3bccffa 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -133,6 +133,10 @@ 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 +checks out the branch (instead of detaching). You may also specify +"`-`" which is synonymous with "`@\{-1\}`". Detached HEAD diff --git a/builtin-checkout.c b/builtin-checkout.c index dc1de06..b0a101b 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -679,6 +679,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) arg = argv[0]; has_dash_dash = (argc > 1) && !strcmp(argv[1], "--"); + if (!strcmp(arg, "-")) + arg = "@{-1}"; + if (get_sha1(arg, rev)) { if (has_dash_dash) /* case (1) */ die("invalid reference: %s", arg); diff --git a/t/t2012-checkout-last.sh b/t/t2012-checkout-last.sh new file mode 100755 index 0000000..320f6eb --- /dev/null +++ b/t/t2012-checkout-last.sh @@ -0,0 +1,50 @@ +#!/bin/sh + +test_description='checkout can switch to last branch' + +. ./test-lib.sh + +test_expect_success 'setup' ' + echo hello >world && + git add world && + git commit -m initial && + git branch other && + echo "hello again" >>world && + git add world && + git commit -m second +' + +test_expect_success '"checkout -" does not work initially' ' + test_must_fail git checkout - +' + +test_expect_success 'first branch switch' ' + git checkout other +' + +test_expect_success '"checkout -" switches back' ' + git checkout - && + test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master" +' + +test_expect_success '"checkout -" switches forth' ' + git checkout - && + test "z$(git symbolic-ref HEAD)" = "zrefs/heads/other" +' + +test_expect_success 'detach HEAD' ' + git checkout $(git rev-parse HEAD) +' + +test_expect_success '"checkout -" attaches again' ' + git checkout - && + test "z$(git symbolic-ref HEAD)" = "zrefs/heads/other" +' + +test_expect_success '"checkout -" detaches again' ' + git checkout - && + test "z$(git rev-parse HEAD)" = "z$(git rev-parse other)" && + test_must_fail git symbolic-ref HEAD +' + +test_done -- cgit v0.10.2-6-g49f6 From c2883e62f5b9980e5402431f2261c961354d0f15 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 19 Jan 2009 00:04:25 -0800 Subject: interpret_nth_last_branch(): avoid traversing the reflog twice You can have quite a many reflog entries, but you typically won't recall which branch you were on after switching branches for more than several times. Instead of reading the reflog twice, this reads the branch switching event and keeps as many entries as the user asked from the latest such entries, which is the minimum required to be able to switch back to the branch we were recently on. [jc: improvements from Dscho squashed in] Signed-off-by: Junio C Hamano diff --git a/sha1_name.c b/sha1_name.c index 9e1538e..d6972f2 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -685,8 +685,7 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1) } struct grab_nth_branch_switch_cbdata { - int counting; - int nth; + long cnt, alloc; struct strbuf *buf; }; @@ -697,6 +696,7 @@ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1, struct grab_nth_branch_switch_cbdata *cb = cb_data; const char *match = NULL, *target = NULL; size_t len; + int nth; if (!prefixcmp(message, "checkout: moving from ")) { match = message + strlen("checkout: moving from "); @@ -711,16 +711,9 @@ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1, if (target[len] == '\n' && !strncmp(match, target, len)) return 0; - if (cb->counting) { - cb->nth++; - return 0; - } - - if (cb->nth-- <= 0) { - strbuf_reset(cb->buf); - strbuf_add(cb->buf, match, len); - return 1; - } + nth = cb->cnt++ % cb->alloc; + strbuf_reset(&cb->buf[nth]); + strbuf_add(&cb->buf[nth], match, len); return 0; } @@ -737,7 +730,8 @@ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1, */ int interpret_nth_last_branch(const char *name, struct strbuf *buf) { - int nth; + long nth; + int i; struct grab_nth_branch_switch_cbdata cb; const char *brace; char *num_end; @@ -750,19 +744,22 @@ int interpret_nth_last_branch(const char *name, struct strbuf *buf) nth = strtol(name+3, &num_end, 10); if (num_end != brace) return -1; - - cb.counting = 1; - cb.nth = 0; - cb.buf = buf; + if (nth <= 0) + return -1; + cb.alloc = nth; + cb.buf = xmalloc(nth * sizeof(struct strbuf)); + for (i = 0; i < nth; i++) + strbuf_init(&cb.buf[i], 20); + cb.cnt = 0; for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb); - - if (cb.nth < nth) + if (cb.cnt < nth) return 0; - - cb.counting = 0; - cb.nth -= nth; - cb.buf = buf; - for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb); + i = cb.cnt % nth; + strbuf_reset(buf); + strbuf_add(buf, cb.buf[i].buf, cb.buf[i].len); + for (i = 0; i < nth; i++) + strbuf_release(&cb.buf[i]); + free(cb.buf); return brace-name+1; } diff --git a/t/t2012-checkout-last.sh b/t/t2012-checkout-last.sh index 320f6eb..87b30a2 100755 --- a/t/t2012-checkout-last.sh +++ b/t/t2012-checkout-last.sh @@ -47,4 +47,48 @@ test_expect_success '"checkout -" detaches again' ' test_must_fail git symbolic-ref HEAD ' +test_expect_success 'more switches' ' + for i in 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 + do + git checkout -b branch$i + done +' + +more_switches () { + for i in 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 + do + git checkout branch$i + done +} + +test_expect_success 'switch to the last' ' + more_switches && + git checkout @{-1} && + test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch2" +' + +test_expect_success 'switch to second from the last' ' + more_switches && + git checkout @{-2} && + test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch3" +' + +test_expect_success 'switch to third from the last' ' + more_switches && + git checkout @{-3} && + test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch4" +' + +test_expect_success 'switch to fourth from the last' ' + more_switches && + git checkout @{-4} && + test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch5" +' + +test_expect_success 'switch to twelfth from the last' ' + more_switches && + git checkout @{-12} && + test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch13" +' + test_done -- cgit v0.10.2-6-g49f6 From aa9c55b66719c86896d134d35de8c263c078a481 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 17 Jan 2009 19:08:12 +0100 Subject: Fix parsing of @{-1}@{1} To do that, Git no longer looks forward for the '@{' corresponding to the closing '}' but backward, and dwim_ref() as well as dwim_log() learnt about the @{-} notation. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/sha1_name.c b/sha1_name.c index d6972f2..9d544a2 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -238,8 +238,28 @@ static int ambiguous_path(const char *path, int len) return slash; } +/* + * *string and *len will only be substituted, and *string returned (for + * later free()ing) if the string passed in is of the form @{-}. + */ +static char *substitute_nth_last_branch(const char **string, int *len) +{ + struct strbuf buf = STRBUF_INIT; + int ret = interpret_nth_last_branch(*string, &buf); + + if (ret == *len) { + size_t size; + *string = strbuf_detach(&buf, &size); + *len = size; + return (char *)*string; + } + + return NULL; +} + int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref) { + char *last_branch = substitute_nth_last_branch(&str, &len); const char **p, *r; int refs_found = 0; @@ -259,11 +279,13 @@ int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref) break; } } + free(last_branch); return refs_found; } int dwim_log(const char *str, int len, unsigned char *sha1, char **log) { + char *last_branch = substitute_nth_last_branch(&str, &len); const char **p; int logs_found = 0; @@ -294,6 +316,7 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log) if (!warn_ambiguous_refs) break; } + free(last_branch); return logs_found; } @@ -312,7 +335,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) /* basic@{time or number or -number} format to query ref-log */ reflog_len = at = 0; if (str[len-1] == '}') { - for (at = 0; at < len - 1; at++) { + for (at = len-2; at >= 0; at--) { if (str[at] == '@' && str[at+1] == '{') { reflog_len = (len-1) - (at+2); len = at; diff --git a/t/t1505-rev-parse-last.sh b/t/t1505-rev-parse-last.sh index 1e49dd2..c745ec4 100755 --- a/t/t1505-rev-parse-last.sh +++ b/t/t1505-rev-parse-last.sh @@ -54,7 +54,7 @@ test_expect_success '@{-1}^2 works' ' test_rev_equivalent side^2 @{-1}^2 ' -test_expect_failure '@{-1}@{1} works' ' +test_expect_success '@{-1}@{1} works' ' test_rev_equivalent side@{1} @{-1}@{1} ' -- cgit v0.10.2-6-g49f6 From c829774c30e10473d3139edf92a4afe36e8abdc2 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 19 Jan 2009 16:44:08 -0800 Subject: Fix reflog parsing for a malformed branch switching entry target can be NULL when we failed to parse the message. Signed-off-by: Junio C Hamano diff --git a/sha1_name.c b/sha1_name.c index 9d544a2..f54b6cb 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -727,7 +727,7 @@ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1, target += 4; } - if (!match) + if (!match || !target) return 0; len = target - match - 4; -- cgit v0.10.2-6-g49f6 From 39765e5941d36f74cec4764d315da0ba5743547c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 19 Jan 2009 21:58:31 -0800 Subject: interpret_nth_last_branch(): plug small memleak Signed-off-by: Junio C Hamano diff --git a/sha1_name.c b/sha1_name.c index f54b6cb..4c0370b 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -754,7 +754,7 @@ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1, int interpret_nth_last_branch(const char *name, struct strbuf *buf) { long nth; - int i; + int i, retval; struct grab_nth_branch_switch_cbdata cb; const char *brace; char *num_end; @@ -774,17 +774,21 @@ int interpret_nth_last_branch(const char *name, struct strbuf *buf) for (i = 0; i < nth; i++) strbuf_init(&cb.buf[i], 20); cb.cnt = 0; + retval = 0; for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb); if (cb.cnt < nth) - return 0; + goto release_return; i = cb.cnt % nth; strbuf_reset(buf); strbuf_add(buf, cb.buf[i].buf, cb.buf[i].len); + retval = brace-name+1; + +release_return: for (i = 0; i < nth; i++) strbuf_release(&cb.buf[i]); free(cb.buf); - return brace-name+1; + return retval; } /* -- cgit v0.10.2-6-g49f6 From 101d15e09712a0183db99d228d975c62970654cf Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 19 Jan 2009 22:18:29 -0800 Subject: Introduce for_each_recent_reflog_ent(). This can be used to scan only the last few kilobytes of a reflog, as a cheap optimization when the data you are looking for is likely to be found near the end of it. The caller is expected to fall back to the full scan if that is not the case. Signed-off-by: Junio C Hamano diff --git a/refs.c b/refs.c index 33ced65..024211d 100644 --- a/refs.c +++ b/refs.c @@ -1453,7 +1453,7 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char * return 1; } -int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data) +int for_each_recent_reflog_ent(const char *ref, each_reflog_ent_fn fn, long ofs, void *cb_data) { const char *logfile; FILE *logfp; @@ -1464,6 +1464,16 @@ int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data) logfp = fopen(logfile, "r"); if (!logfp) return -1; + + if (ofs) { + struct stat statbuf; + if (fstat(fileno(logfp), &statbuf) || + statbuf.st_size < ofs || + fseek(logfp, -ofs, SEEK_END) || + fgets(buf, sizeof(buf), logfp)) + return -1; + } + while (fgets(buf, sizeof(buf), logfp)) { unsigned char osha1[20], nsha1[20]; char *email_end, *message; @@ -1497,6 +1507,11 @@ int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data) return ret; } +int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data) +{ + return for_each_recent_reflog_ent(ref, fn, 0, cb_data); +} + static int do_for_each_reflog(const char *base, each_ref_fn fn, void *cb_data) { DIR *dir = opendir(git_path("logs/%s", base)); diff --git a/refs.h b/refs.h index 06ad260..3bb529d 100644 --- a/refs.h +++ b/refs.h @@ -60,6 +60,7 @@ extern int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned /* iterate over reflog entries */ typedef int each_reflog_ent_fn(unsigned char *osha1, unsigned char *nsha1, const char *, unsigned long, int, const char *, void *); int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data); +int for_each_recent_reflog_ent(const char *ref, each_reflog_ent_fn fn, long, void *cb_data); /* * Calls the specified function for each reflog file until it returns nonzero, diff --git a/sha1_name.c b/sha1_name.c index 4c0370b..38c9f1b 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -775,7 +775,13 @@ int interpret_nth_last_branch(const char *name, struct strbuf *buf) strbuf_init(&cb.buf[i], 20); cb.cnt = 0; retval = 0; - for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb); + for_each_recent_reflog_ent("HEAD", grab_nth_branch_switch, 40960, &cb); + if (cb.cnt < nth) { + cb.cnt = 0; + for (i = 0; i < nth; i++) + strbuf_release(&cb.buf[i]); + for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb); + } if (cb.cnt < nth) goto release_return; i = cb.cnt % nth; -- cgit v0.10.2-6-g49f6 From d7c03c1ff98be1d22dd18b70669ffc6fb76b39b3 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 21 Jan 2009 00:37:38 -0800 Subject: Simplify parsing branch switching events in reflog We only accept "checkout: moving from A to B" newer style reflog entries, in order to pick up A. There is no point computing where B begins at after running strstr to locate " to ", nor adding 4 and then subtracting 4 from the same pointer. Signed-off-by: Junio C Hamano diff --git a/sha1_name.c b/sha1_name.c index 38c9f1b..7d95bbb 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -723,17 +723,13 @@ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1, if (!prefixcmp(message, "checkout: moving from ")) { match = message + strlen("checkout: moving from "); - if ((target = strstr(match, " to ")) != NULL) - target += 4; + target = strstr(match, " to "); } if (!match || !target) return 0; - len = target - match - 4; - if (target[len] == '\n' && !strncmp(match, target, len)) - return 0; - + len = target - match; nth = cb->cnt++ % cb->alloc; strbuf_reset(&cb->buf[nth]); strbuf_add(&cb->buf[nth], match, len); -- cgit v0.10.2-6-g49f6 From 73ff1a131b340633b2ec2a0e68490de721448f56 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Sat, 24 Jan 2009 23:23:14 +0100 Subject: t1505: remove debugging cruft Remove a call to git-log that I introduced for debugging and that accidentally made it into d18ba22 (sha1_name: support @{-N} syntax in get_sha1(), 2009-01-17). Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano diff --git a/t/t1505-rev-parse-last.sh b/t/t1505-rev-parse-last.sh index c745ec4..d709ecf 100755 --- a/t/t1505-rev-parse-last.sh +++ b/t/t1505-rev-parse-last.sh @@ -32,8 +32,6 @@ test_expect_success 'setup' ' # # and 'side' should be the last branch -git log --graph --all --pretty=oneline --decorate - test_rev_equivalent () { git rev-parse "$1" > expect && -- cgit v0.10.2-6-g49f6