#include <bits/stdc++.h>
using namespace std;
namespace IO {
const int MAXSIZE = 1 << 18 | 1;
char buf[MAXSIZE], *p1, *p2;
inline int Gc() {
  return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, MAXSIZE, stdin), p1 == p2)
             ? EOF
             : *p1++;
}
template <typename T>
inline void read(T& x) {
  x = 0;
  int f = 0, ch = Gc();
  while (!isdigit(ch)) f |= ch == '-', ch = Gc();
  while (isdigit(ch)) x = x * 10 + ch - '0', ch = Gc();
  if (f) x = -x;
}
}  // namespace IO
using IO::read;
const int MAXN = 2e5 + 5, LOG = 31;
struct Modify {
  int u, v, w, L, R;
} M[MAXN << 1];
struct Ask {
  int u, v;
} Q[MAXN];
int n, m, K;
int Ans[MAXN];
stack<pair<int, int> > stk;
map<pair<int, int>, int> Idx;
struct LinerBasis {
  int A[LOG];
  LinerBasis() { memset(A, 0, sizeof A); }
  inline void insert(int val) {
    for (int i = LOG - 1; i >= 0; --i)
      if ((val >> i) & 1) {
        if (!A[i]) {
          A[i] = val;
          break;
        }
        val ^= A[i];
      }
  }
  inline int Qry(int val) {
    for (int i = LOG - 1; i >= 0; --i) val = min(val, val ^ A[i]);
    return val;
  }
};
namespace DSU {
int fa[MAXN], rnk[MAXN], dist[MAXN];
inline void init() {
  for (int i = 1; i <= n; ++i) fa[i] = i, rnk[i] = 1;
}
inline int findfa(int u) {
  while (u != fa[u]) u = fa[u];
  return u;
}
inline int finddist(int u) {
  int ret = 0;
  while (u != fa[u]) ret ^= dist[u], u = fa[u];
  return ret;
}
}  // namespace DSU
namespace SGT {
using namespace DSU;
vector<int> dat[MAXN << 2];
void Mdy(int nd, int L, int R, const int& opL, const int& opR, const int& idx) {
  if (opL <= L && R <= opR) return void(dat[nd].push_back(idx));
  int Mid = (L + R) / 2;
  if (opL <= Mid) Mdy((nd << 1), L, Mid, opL, opR, idx);
  if (opR > Mid) Mdy((nd << 1 | 1), Mid + 1, R, opL, opR, idx);
}
void divide(int nd, int L, int R, LinerBasis S) {
  if (L > R) return;
  size_t k = stk.size();
  for (size_t i = 0; i < dat[nd].size(); ++i) {
    const int& idx = dat[nd][i];
    int fu = findfa(M[idx].u), fv = findfa(M[idx].v);
    int w = finddist(M[idx].u) ^ finddist(M[idx].v);
    if (fu == fv) {
      S.insert(w ^ M[idx].w);
      continue;
    }
    if (rnk[fu] < rnk[fv]) swap(fu, fv);
    fa[fv] = fu, dist[fv] = w ^ M[idx].w, rnk[fu] += rnk[fu] == rnk[fv];
    stk.push(pair<int, int>(fv, rnk[fu] == rnk[fv]));
  }
  if (L == R)
    Ans[L] = S.Qry(finddist(Q[L].u) ^ finddist(Q[L].v));
  else {
    int Mid = (L + R) / 2;
    divide((nd << 1), L, Mid, S), divide((nd << 1 | 1), Mid + 1, R, S);
  }
  while (stk.size() > k) {
    pair<int, int> u = stk.top();
    stk.pop();
    dist[u.first] = 0, rnk[fa[u.first]] -= u.second, fa[u.first] = u.first;
  }
}
}  // namespace SGT
int main() {
  read(n), read(m);
  for (int u, v, w, i = 1; i <= m; ++i) {
    read(u), read(v), read(w);
    if (u > v) swap(u, v);
    Idx[pair<int, int>(u, v)] = i;
    M[i] = (Modify){u, v, w, 1, -1};
  }
  read(K);
  int Time = 0;
  for (int i = 1; i <= K; ++i) {
    static int opt, x, y, d, idx;
    read(opt), read(x), read(y);
    if (x > y) swap(x, y);
    switch (opt) {
      case 1:
        read(d);
        M[++m] = (Modify){x, y, d, Time + 1, -1};
        Idx[pair<int, int>(x, y)] = m;
        break;
      case 2:
        idx = Idx[pair<int, int>(x, y)], M[idx].R = Time;
        Idx[pair<int, int>(x, y)] = 0;
        break;
      case 3:
        Q[++Time] = (Ask){x, y};
        break;
      default:
        fprintf(stderr, "GG\n");
    }
  }
  for (int i = 1; i <= m; ++i) {
    if (M[i].R == -1) M[i].R = Time;
    if (M[i].L <= M[i].R) SGT::Mdy(1, 1, Time, M[i].L, M[i].R, i);
  }
  DSU::init();
  LinerBasis tmp;
  SGT::divide(1, 1, Time, tmp);
  for (int i = 1; i <= Time; ++i) printf("%d\n", Ans[i]);
  return 0;
}
