File: Mercurial.cpp

package info (click to toggle)
watchman 4.9.0-9
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 9,992 kB
  • sloc: cpp: 27,459; python: 6,538; java: 3,404; php: 3,257; ansic: 2,803; javascript: 1,116; makefile: 671; ruby: 364; sh: 124; xml: 102; lisp: 4
file content (160 lines) | stat: -rw-r--r-- 4,379 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/* Copyright 2017-present Facebook, Inc.
 * Licensed under the Apache License, Version 2.0 */
#include "Mercurial.h"
#include "ChildProcess.h"
#include "Logging.h"
#include "watchman.h"

// Capability indicating support for the mercurial SCM
W_CAP_REG("scm-hg")

namespace watchman {

using Options = ChildProcess::Options;

Mercurial::infoCache::infoCache(std::string path) : dirStatePath(path) {
  memset(&dirstate, 0, sizeof(dirstate));
}

w_string Mercurial::infoCache::lookupMergeBase(const std::string& commitId) {
  if (dotChanged()) {
    watchman::log(
        watchman::DBG, "Blowing mergeBases cache because dirstate changed\n");

    mergeBases.clear();
    return nullptr;
  }

  auto it = mergeBases.find(commitId);
  if (it != mergeBases.end()) {
    watchman::log(watchman::DBG, "mergeBases cache hit for ", commitId, "\n");
    return it->second;
  }
  watchman::log(watchman::DBG, "mergeBases cache miss for ", commitId, "\n");

  return nullptr;
}

bool Mercurial::infoCache::dotChanged() {
  bool result;

  try {
    auto info = getFileInformation(dirStatePath.c_str(),
                                   CaseSensitivity::CaseSensitive);

    if (info.size != dirstate.size ||
        info.mtime.tv_sec != dirstate.mtime.tv_sec ||
        info.mtime.tv_nsec != dirstate.mtime.tv_nsec) {
      log(DBG, "mergeBases stat(", dirStatePath, ") info differs\n");
      result = true;
    } else {
      result = false;
      log(DBG, "mergeBases stat(", dirStatePath, ") info same\n");
    }

    dirstate = info;

  } catch (const std::system_error &exc) {
    // Failed to stat, so assume that it changed
    log(DBG, "mergeBases stat(", dirStatePath, ") failed: ", exc.what(), "\n");
    result = true;
  }
  return result;
}

Mercurial::Mercurial(w_string_piece rootPath, w_string_piece scmRoot)
    : SCM(rootPath, scmRoot),
      cache_(infoCache(to<std::string>(getSCMRoot(), "/.hg/dirstate"))) {}

w_string Mercurial::mergeBaseWith(w_string_piece commitId) const {
  std::string idString(commitId.data(), commitId.size());

  {
    auto cache = cache_.wlock();
    auto result = cache->lookupMergeBase(idString);
    if (result) {
      watchman::log(
          watchman::DBG,
          "Using cached mergeBase value of ",
          result,
          " for commitId ",
          commitId,
          " because dirstate file is unchanged\n");
      return result;
    }
  }

  Options opt;
  // Ensure that the hgrc doesn't mess with the behavior
  // of the commands that we're runing.
  opt.environment().set("HGPLAIN", w_string("1"));
  opt.nullStdin();
  opt.pipeStdout();
  opt.pipeStderr();
  opt.chdir(getRootPath());

  auto revset = to<std::string>("ancestor(.,", commitId, ")");
  ChildProcess proc(
      {"hg", "log", "-T", "{node}", "-r", revset}, std::move(opt));

  auto outputs = proc.communicate();
  auto status = proc.wait();

  if (status) {
    throw std::runtime_error(to<std::string>(
        "failed query for the merge base; command returned with status ",
        status,
        ", output=",
        outputs.first,
        " error=",
        outputs.second));
  }

  if (outputs.first.size() != 40) {
    throw std::runtime_error(to<std::string>(
        "expected merge base to be a 40 character string, got ",
        outputs.first));
  }

  {
    auto cache = cache_.wlock();
    cache->mergeBases[idString] = outputs.first;
  }
  return outputs.first;
}

std::vector<w_string> Mercurial::getFilesChangedSinceMergeBaseWith(
    w_string_piece commitId) const {
  Options opt;
  // Ensure that the hgrc doesn't mess with the behavior
  // of the commands that we're runing.
  opt.environment().set("HGPLAIN", w_string("1"));
  opt.nullStdin();
  opt.pipeStdout();
  opt.pipeStderr();
  opt.chdir(getRootPath());

  // The "" argument at the end causes paths to be printed out relative to the
  // cwd (set to root path above).
  ChildProcess proc(
      {"hg", "status", "-n", "--rev", commitId, ""}, std::move(opt));

  auto outputs = proc.communicate();

  std::vector<w_string> lines;
  w_string_piece(outputs.first).split(lines, '\n');

  auto status = proc.wait();
  if (status) {
    throw std::runtime_error(to<std::string>(
        "failed query for the hg status; command returned with status ",
        status,
        " out=",
        outputs.first,
        " err=",
        outputs.second));
  }

  return lines;
}
}