def main(): from sys import stdin, setrecursionlimit # setrecursionlimit(1000000) input = stdin.readline def iinput(): return int(input()) def sinput(): return input().rstrip() def i0input(): return int(input()) - 1 def linput(): return list(input().split()) def liinput(): return list(map(int, input().split())) def miinput(): return map(int, input().split()) def li0input(): return list(map(lambda x: int(x) - 1, input().split())) def mi0input(): return map(lambda x: int(x) - 1, input().split()) INF = 1000000000000000000 MOD = 1000000007 class UnionFindTree: def __init__(self, initial_size:int) -> None: self.n_nodes = initial_size self.parents = [i for i in range(initial_size)] self.ranks = [0 for i in range(initial_size)] self.size = [1 for i in range(initial_size)] self.n_roots = initial_size def root(self, n:int) -> int: if self.parents[n] == n: return n else: self.parents[n] = self.root(self.parents[n]) return self.parents[n] def same(self, n:int, m:int) -> bool: return (self.root(n) == self.root(m)) def unite(self, n:int, m:int) -> None: if self.same(n, m): return self.n_roots -= 1 n, m = self.root(n), self.root(m) if self.ranks[n] < self.ranks[m]: self.parents[n] = m self.size[m] += self.size[n] else: self.parents[m] = n self.size[n] += self.size[m] if self.ranks[n] == self.ranks[m]: self.ranks[n] += 1 def get_roots(self) -> set: return set([self.root(x) for x in range(self.n_nodes)]) def count_roots(self) -> int: return self.n_roots def get_tree_size(self, n:int) -> int: return self.size[self.root(n)] N, M = miinput() uf = UnionFindTree(N) UV = [] for _ in [0] * M: u, v = mi0input() UV.append((u, v)) ctr = [0] * N for u, v in UV[::-1]: u = uf.root(u) v = uf.root(v) if u == v: ctr[u] += 1 else: uf.unite(u, v) r = uf.root(u) ctr[r] = max(ctr[u], ctr[v]) + 1 print(max(ctr)) main()