forked from ezyang/ghstack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdiff.py
More file actions
117 lines (94 loc) · 3.92 KB
/
diff.py
File metadata and controls
117 lines (94 loc) · 3.92 KB
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
#!/usr/bin/env python3
import re
from dataclasses import dataclass
from typing import Optional, Pattern
from ghstack.types import GitHubNumber, GitTreeHash
RE_GH_METADATA = re.compile(
r"gh-metadata: (?P<owner>[^/]+) (?P<repo>[^/]+) (?P<number>[0-9]+) "
r"gh/(?P<username>[a-zA-Z0-9-]+)/(?P<ghnum>[0-9]+)/head",
re.MULTILINE,
)
RAW_PULL_REQUEST_RESOLVED = (
r"(Pull Request resolved|Pull-Request-resolved|Pull-Request): "
r"https://{github_url}/(?P<owner>[^/]+)/(?P<repo>[^/]+)/pull/(?P<number>[0-9]+)"
)
def re_pull_request_resolved(github_url: str) -> Pattern[str]:
return re.compile(RAW_PULL_REQUEST_RESOLVED.format(github_url=github_url))
def re_pull_request_resolved_w_sp(github_url: str) -> Pattern[str]:
return re.compile(r"\n*" + RAW_PULL_REQUEST_RESOLVED.format(github_url=github_url))
@dataclass
class PullRequestResolved:
owner: str
repo: str
number: GitHubNumber
github_url: str
def url(self) -> str:
return "https://{}/{}/{}/pull/{}".format(
self.github_url, self.owner, self.repo, self.number
)
@staticmethod
def search(s: str, github_url: str) -> Optional["PullRequestResolved"]:
m = re_pull_request_resolved(github_url).search(s)
if m is not None:
return PullRequestResolved(
owner=m.group("owner"),
repo=m.group("repo"),
number=GitHubNumber(int(m.group("number"))),
github_url=github_url,
)
m = RE_GH_METADATA.search(s)
if m is not None:
return PullRequestResolved(
owner=m.group("owner"),
repo=m.group("repo"),
number=GitHubNumber(int(m.group("number"))),
github_url=github_url,
)
return None
@dataclass
class Diff:
"""
An abstract representation of a diff. Typically represents git commits,
but we may also virtually be importing diffs from other VCSes, hence
the agnosticism.
"""
# Title of the diff
title: str
# Detailed description of the diff. Includes the title.
summary: str
# Unique identifier representing the commit in question (may be a
# Git/Mercurial commit hash; the important thing is that it can be
# used as a unique identifier.)
oid: str
# Unique identifier representing the commit in question, but it
# is *invariant* to changes in commit message / summary. In Git,
# a valid identifier would be the tree hash of the commit (rather
# than the commit hash itself); in Phabricator it could be the
# version of the diff.
#
# It is OK for this source id to wobble even if the tree stays the
# same. This simply means we will think there are changes even
# if there aren't any, which should be safe (but just generate
# annoying updates). What we would like is for the id to quiesce:
# if you didn't rebase your hg rev, the source id is guaranteed to
# be the same.
source_id: str
# The contents of 'Pull-Request'. This is None for
# diffs that haven't been submitted by ghstack. For BC reasons,
# this also accepts gh-metadata.
pull_request_resolved: Optional[PullRequestResolved]
# A git tree hash that represents the contents of this diff, if it
# were applied in Git.
#
# TODO: Constructing these tree hashes if they're not already in Git
# is a somewhat involved process, as you have to actually construct
# the git tree object (it's not guaranteed to exist already). I'm
# offloading this work onto the ghimport/ghexport tools.
tree: GitTreeHash
# The name and email of the author, used so we can preserve
# authorship information when constructing a rebased commit
author_name: Optional[str]
author_email: Optional[str]
# If this isn't actually a diff; it's a boundary commit (not part
# of the stack) that we've parsed for administrative purposes
boundary: bool