Commits

Greg Ward committed da4a85c Merge

merge

Comments (0)

Files changed (2)

+#include <errno.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+
+#if defined __BEOS__ && !defined __HAIKU__
+#include <ByteOrder.h>
+#else
+#include <arpa/inet.h>
+#endif
+
 #include "common.h"
 #include "hg.h"
 
+#define NODEID_LEN 20
+
 static int
 hg_probe(vccontext_t* context)
 {
     return sum;
 }
 
+static int is_revlog_inlined(FILE *f)
+{
+    const unsigned int REVLOGNGINLINEDATA = 1 << 16;
+    int revlog_ver;
+    size_t origpos, rlen;
+
+    origpos = ftell(f);
+    rlen = fread(&revlog_ver, sizeof(revlog_ver), 1, f);
+    fseek(f, origpos, SEEK_SET);
+
+    revlog_ver = ntohl(revlog_ver);
+    return (rlen == 1) ? revlog_ver & REVLOGNGINLINEDATA : 0;
+}
+
+typedef struct {
+	char nodeid[NODEID_LEN];
+	int rev;
+	int istip;
+} csinfo_t;
+
+//! get changeset info for the specified nodeid
+static csinfo_t get_csinfo(const char* nodeid)
+{
+    // only supports RevlogNG. See mercurial/parsers.c for details.
+    const char* REVLOG_FILENAME = ".hg/store/00changelog.i";
+    const size_t ENTRY_LEN = 64, COMP_LEN_OFS = 8, NODEID_OFS = 32;
+
+    char buf[ENTRY_LEN];
+    FILE* f;
+    int inlined;
+    csinfo_t csinfo = {"", -1, 0};
+    int i;
+
+    f = fopen(REVLOG_FILENAME, "rb");
+    if (!f) {
+        debug("error opening '%s': %s", REVLOG_FILENAME, strerror(errno));
+        return csinfo;
+    }
+
+    inlined = is_revlog_inlined(f);
+
+    for (i = 0; !feof(f); ++i) {
+        size_t comp_len, rlen;
+
+        rlen = fread(buf, 1, ENTRY_LEN, f);
+        if (rlen == 0) break;
+        if (rlen != ENTRY_LEN) {
+            debug("error while reading '%s': incomplete entry (read = %d)",
+                  REVLOG_FILENAME, rlen);
+            break;
+        }
+
+        // already found node but it's not the last one
+        if (csinfo.rev >= 0) {
+            csinfo.istip = 0;
+            break;
+        }
+
+        comp_len = ntohl(*((uint32_t *) (buf + COMP_LEN_OFS)));
+        if (memcmp(nodeid, buf + NODEID_OFS, NODEID_LEN) == 0) {
+            memcpy(csinfo.nodeid, buf + NODEID_OFS, NODEID_LEN);
+            csinfo.rev = i;  // FIXME
+            csinfo.istip = 1;
+        }
+
+        if (inlined) fseek(f, comp_len, SEEK_CUR);
+    }
+
+    fclose(f);
+    return csinfo;
+}
+
+static size_t get_mq_patchname(char* str, const char* nodeid, size_t n)
+{
+    char buf[1024];
+
+    if (read_last_line(".hg/patches/status", buf, 1024)) {
+        char nodeid_s[NODEID_LEN * 2 + 1], *p, *patch, *patch_nodeid_s;
+        dump_hex(nodeid, nodeid_s, NODEID_LEN);
+
+        debug("read last line from .hg/patches/status: '%s'", buf);
+        p = strchr(buf, ':');
+        if (!p) return 0;
+        *p = '\0';
+        patch_nodeid_s = buf;
+        patch = p + 1;
+        debug("patch name found: '%s', nodeid: %s", patch, patch_nodeid_s);
+
+        if (strcmp(patch_nodeid_s, nodeid_s)) return 0;
+
+        strncpy(str, patch, n);
+        str[n - 1] = '\0';
+        return strlen(str);
+    }
+    else {
+        debug("failed to read from .hg/patches/status: assuming no mq patch applied");
+        return 0;
+    }
+}
+
+static size_t put_nodeid(char* str, const char* nodeid)
+{
+    const size_t SHORT_NODEID_LEN = 6;  // size in binary repr
+    char buf[512], *p = str;
+    size_t n;
+
+    csinfo_t csinfo = get_csinfo(nodeid);
+
+    if (csinfo.rev >= 0) p += sprintf(p, "%d:", csinfo.rev);
+
+    dump_hex(nodeid, p, SHORT_NODEID_LEN);
+    p += SHORT_NODEID_LEN * 2;
+
+    n = get_mq_patchname(buf, nodeid, sizeof(buf));
+    if (n) {
+        *p = '['; ++p;
+        memcpy(p, buf, n); p += n;
+        *p = ']'; ++p;
+        *p = '\0';
+    } else {
+        if (csinfo.istip) {
+            strcpy(p, "[tip]");
+            p += 5;
+        }
+    }
+
+    return p - str;
+}
+
 static void
 update_nodeid(vccontext_t* context, result_t* result)
 {
-    const size_t NODEID_LEN = 20;
     char buf[NODEID_LEN * 2];
     char rev[32];
     size_t readsize;
 
     readsize = read_file(".hg/dirstate", buf, NODEID_LEN * 2);
     if (readsize == NODEID_LEN * 2) {
+        char destbuf[1024] = {'\0'};
+        char* p = destbuf;
         debug("read nodeids from .hg/dirstate");
 
         // first parent
-        if (!sum_bytes((unsigned char *) buf, NODEID_LEN)) goto push;
-        dump_hex(buf, rev, 6);
+        if (sum_bytes((unsigned char *) buf, NODEID_LEN)) {
+            p += put_nodeid(p, buf);
+
+        }
 
         // second parent
-        if (!sum_bytes((unsigned char *) buf + NODEID_LEN, NODEID_LEN)) goto push;
-        rev[12] = ',';
-        dump_hex(buf + NODEID_LEN, rev + 13, 6);
+        if (sum_bytes((unsigned char *) buf + NODEID_LEN, NODEID_LEN)) {
+            *p = ','; ++p;
+            p += put_nodeid(p, buf + NODEID_LEN);
+        }
+
+        result_set_revision(result, destbuf, -1);
     }
     else {
         debug("failed to read from .hg/dirstate");
-        return;
-    }
-
-push:
-    result_set_revision(result, rev, -1);
-}
-
-static void
-update_mq_info(vccontext_t* context, result_t* result)
-{
-    char buf[1024], *patch;
-
-    // we treat the name of the mq patch as the revision
-    if (!context->options->show_revision) return;
-
-    if (read_last_line(".hg/patches/status", buf, 1024)) {
-        debug("read last line from .hg/patches/status: '%s'", buf);
-        patch = strchr(buf, ':');
-        if (!patch) return;
-        patch += 1;
-        debug("patch name found: '%s'", patch);
-        result_set_revision(result, patch, -1);
-    }
-    else {
-        debug("failed to read from .hg/patches/status: assuming no mq patch applied");
     }
 }
 
         result_set_branch(result, "default");
     }
 
-    update_mq_info(context, result);
-    if (!result->revision) update_nodeid(context, result);
+    update_nodeid(context, result);
 
     return result;
 }

tests/test-simple

     touch .hg/patches/status
     assert_vcprompt "hg_mq applied 0" "hg:/foo" "%n:%r/%b"
 
-    echo '6177348a28081b6c12ef98bf564d9b6d052fe852:bar.diff' >> .hg/patches/status
-    assert_vcprompt "hg_mq applied 1" "hg:bar.diff/foo" "%n:%r/%b"
+    printf '0123456789abcdefghij\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0' \
+        > .hg/dirstate
+    echo '303132333435363738396162636465666768696a:bar.diff' >> .hg/patches/status
+    assert_vcprompt "hg_mq applied 1" "hg:303132333435[bar.diff]/foo" "%n:%r/%b"
 
-    echo 'b03082a7915ea408d1ebb01bedda9a665a8bcbd7:baz.diff' >> .hg/patches/status
-    assert_vcprompt "hg_mq applied 2" "hg:baz.diff/foo" "%n:%r/%b"
+    printf 'a123456789abcdefghij\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0' \
+        > .hg/dirstate
+    echo '613132333435363738396162636465666768696a:baz.diff' >> .hg/patches/status
+    assert_vcprompt "hg_mq applied 2" "hg:613132333435[baz.diff]/foo" "%n:%r/%b"
+}
+
+test_simple_hg_revlog ()
+{
+    cd $tmpdir
+    mkdir hg_tip && cd hg_tip
+    mkdir .hg .hg/store
+
+    # not inlined
+    (
+        printf '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'
+        printf '0123456789abcdefghij\0\0\0\0\0\0\0\0\0\0\0\0'
+
+        printf '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'
+        printf 'a123456789abcdefghij\0\0\0\0\0\0\0\0\0\0\0\0'
+    ) > .hg/store/00changelog.i
 
     printf '0123456789abcdefghij\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0' \
         > .hg/dirstate
-    assert_vcprompt "hg_mq override nodeid" "hg:baz.diff" "%n:%r"
+    assert_vcprompt "hg_revlog notip" "hg:0:303132333435" "%n:%r"
+
+    printf 'a123456789abcdefghij\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0' \
+        > .hg/dirstate
+    assert_vcprompt "hg_revlog tip" "hg:1:613132333435[tip]" "%n:%r"
+
+    # inlined
+    (
+        printf '\0\001\0\0\0\0\0\0\0\0\0\002\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'
+        printf '0123456789abcdefghij\0\0\0\0\0\0\0\0\0\0\0\0'
+        printf '\0\0'  # inlined data
+
+        printf '\0\0\0\0\0\0\0\0\0\0\0\001\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'
+        printf 'a123456789abcdefghij\0\0\0\0\0\0\0\0\0\0\0\0'
+        printf '\0'  # inlined data
+    ) > .hg/store/00changelog.i
+
+    printf '0123456789abcdefghij\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0' \
+        > .hg/dirstate
+    assert_vcprompt "hg_revlog inlined notip" "hg:0:303132333435" "%n:%r"
+
+    printf 'a123456789abcdefghij\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0' \
+        > .hg/dirstate
+    assert_vcprompt "hg_revlog inlined tip" "hg:1:613132333435[tip]" "%n:%r"
 }
 
 test_simple_svn()
 test_simple_hg
 test_simple_hg_bookmarks
 test_simple_hg_mq
+test_simple_hg_revlog
 test_simple_svn
 test_xml_svn
 test_truncated_svn
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.