summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThibault Saunier <thibault.saunier@osg.samsung.com>2017-02-03 14:04:14 -0300
committerThibault Saunier <thibault.saunier@osg.samsung.com>2017-03-21 15:10:42 -0300
commitbdce4b379d68835c367948eac3e093465ff1163f (patch)
treec3aa3056c268870e397c2f1e58fd3df5aac94548
parent7575cd81ef1712ae39030282773c7c1facab6282 (diff)
Allow stashing changes before applying/attaching/landing patches
Avoid to require the user to do it himself and making the worklow less painful. Very similare to git.rebase.autostash and friends. Differential Revision: https://phabricator.freedesktop.org/D1641
-rwxr-xr-xgit-phab84
1 files changed, 71 insertions, 13 deletions
diff --git a/git-phab b/git-phab
index 0e38619..d7ceb1e 100755
--- a/git-phab
+++ b/git-phab
@@ -22,6 +22,7 @@
22# http://www.gnu.org/licenses/. 22# http://www.gnu.org/licenses/.
23 23
24import base64 24import base64
25import configparser
25import logging 26import logging
26import socket 27import socket
27import tempfile 28import tempfile
@@ -78,6 +79,39 @@ class Colors:
78 cls.ENDC = '\033[0m' 79 cls.ENDC = '\033[0m'
79 80
80 81
82def stash(func):
83 def wrapper(self, *args):
84 needs_stash = self.repo.is_dirty()
85 if needs_stash:
86 if not self.autostash:
87 self.die(
88 "Repository is dirty. Aborting.\n"
89 "You can use `--autostash` to automatically"
90 " stash uncommitted changes\n"
91 "You can also `git config [--global] phab.autostash true`"
92 " to make it permanent")
93 print("Stashing current changes before attaching patches")
94 self.repo.git.stash()
95 try:
96 func(self, *args)
97 finally:
98 if needs_stash:
99 print("Restoring stashed changes")
100 stash_name = "stash@{0}"
101 if self.repo.is_dirty():
102 # This might happen if some linting tool starts
103 # changing the code.
104 stash_name = "stash@{1}"
105 print("Some more changes have been done"
106 " during the process, stashing them"
107 " and going back to the state before attaching.\n"
108 " You can see those with `git stash show stash@{0}`")
109 self.repo.git.stash()
110 self.repo.git.stash('pop', stash_name)
111
112 return wrapper
113
114
81class GitPhab: 115class GitPhab:
82 116
83 def __init__(self): 117 def __init__(self):
@@ -92,6 +126,7 @@ class GitPhab:
92 self.output_directory = None 126 self.output_directory = None
93 self.phab_repo = None 127 self.phab_repo = None
94 self.staging_url = None 128 self.staging_url = None
129 self.autostash = False
95 130
96 self.repo = git.Repo(os.getcwd(), search_parent_directories=True) 131 self.repo = git.Repo(os.getcwd(), search_parent_directories=True)
97 self.read_arcconfig() 132 self.read_arcconfig()
@@ -538,6 +573,13 @@ Paste API Token from that page and press <enter>: """ % self.phabricator_uri)
538 573
539 if self.remote: 574 if self.remote:
540 self.validate_remote() 575 self.validate_remote()
576
577 try:
578 self.autostash |= self.repo.config_reader().get_value(
579 'phab', 'autostash')
580 except configparser.NoOptionError:
581 pass
582
541 # Try to guess the task from branch name 583 # Try to guess the task from branch name
542 if self.repo.head.is_detached: 584 if self.repo.head.is_detached:
543 self.die("HEAD is currently detached. Aborting.") 585 self.die("HEAD is currently detached. Aborting.")
@@ -915,10 +957,12 @@ Paste API Token from that page and press <enter>: """ % self.phabricator_uri)
915 filetype = "3" 957 filetype = "3"
916 metadata = { 958 metadata = {
917 "old:file:size": diff.a_blob.size if diff.a_blob else 0, 959 "old:file:size": diff.a_blob.size if diff.a_blob else 0,
918 "old:file:mime-type": diff.a_blob.mime_type if diff.a_blob else '', 960 "old:file:mime-type": diff.a_blob.mime_type if diff.a_blob else
961 '',
919 "old:binary-phid": a_phab_file.response if a_phab_file else '', 962 "old:binary-phid": a_phab_file.response if a_phab_file else '',
920 "new:file:size": diff.b_blob.size if diff.b_blob else 0, 963 "new:file:size": diff.b_blob.size if diff.b_blob else 0,
921 "new:file:mime-type": diff.b_blob.mime_type if diff.b_blob else '', 964 "new:file:mime-type": diff.b_blob.mime_type if diff.b_blob else
965 '',
922 "new:binary-phid": b_phab_file.response if b_phab_file else '', 966 "new:binary-phid": b_phab_file.response if b_phab_file else '',
923 } 967 }
924 968
@@ -1060,8 +1104,8 @@ Paste API Token from that page and press <enter>: """ % self.phabricator_uri)
1060 # (avoiding making query on the server when not needed) 1104 # (avoiding making query on the server when not needed)
1061 if last_revision_id and \ 1105 if last_revision_id and \
1062 self.repo.head.commit.parents[0] not in proposed_commits and \ 1106 self.repo.head.commit.parents[0] not in proposed_commits and \
1063 not self.phabricator.differential.query(ids=[last_revision_id], 1107 not self.phabricator.differential.query(
1064 status="status-closed"): 1108 ids=[last_revision_id], status="status-closed"):
1065 body.append("Depends on D%s" % last_revision_id) 1109 body.append("Depends on D%s" % last_revision_id)
1066 phab_fields.append("Projects: %s" % ','.join(self.project_phids)) 1110 phab_fields.append("Projects: %s" % ','.join(self.project_phids))
1067 1111
@@ -1139,10 +1183,8 @@ Paste API Token from that page and press <enter>: """ % self.phabricator_uri)
1139 diffid=diff.diffid, 1183 diffid=diff.diffid,
1140 message=message), diff 1184 message=message), diff
1141 1185
1186 @stash
1142 def do_attach(self): 1187 def do_attach(self):
1143 if self.repo.is_dirty():
1144 self.die("Repository is dirty. Aborting.")
1145
1146 # If we are in branch "T123" and user does "git phab attach -t T456", 1188 # If we are in branch "T123" and user does "git phab attach -t T456",
1147 # that's suspicious. Better stop before doing a mistake. 1189 # that's suspicious. Better stop before doing a mistake.
1148 if self.branch_task and self.branch_task != self.task: 1190 if self.branch_task and self.branch_task != self.task:
@@ -1571,10 +1613,9 @@ Paste API Token from that page and press <enter>: """ % self.phabricator_uri)
1571 os.unlink(filename) 1613 os.unlink(filename)
1572 n += 1 1614 n += 1
1573 1615
1616 @stash
1574 def do_apply(self): 1617 def do_apply(self):
1575 if self.repo.is_dirty(): 1618 if not self.differential and not self.task:
1576 self.die("Repository is dirty. Aborting.")
1577 elif not self.differential and not self.task:
1578 self.die("No task or revision provided. Aborting.") 1619 self.die("No task or revision provided. Aborting.")
1579 1620
1580 if self.differential: 1621 if self.differential:
@@ -1836,10 +1877,8 @@ Paste API Token from that page and press <enter>: """ % self.phabricator_uri)
1836 1877
1837 print(" -> Branch %s was deleted" % branch.name) 1878 print(" -> Branch %s was deleted" % branch.name)
1838 1879
1880 @stash
1839 def do_land(self): 1881 def do_land(self):
1840 if self.repo.is_dirty():
1841 self.die("Repository is dirty. Aborting.")
1842
1843 if self.task: 1882 if self.task:
1844 commit, remote, remote_branch_name = self.fetch_from_task() 1883 commit, remote, remote_branch_name = self.fetch_from_task()
1845 branch = self.repo.active_branch 1884 branch = self.repo.active_branch
@@ -1940,6 +1979,7 @@ def check_dependencies_versions():
1940 git.__version__, Colors.ENDC)) 1979 git.__version__, Colors.ENDC))
1941 exit(1) 1980 exit(1)
1942 1981
1982
1943if __name__ == '__main__': 1983if __name__ == '__main__':
1944 check_dependencies_versions() 1984 check_dependencies_versions()
1945 parser = argparse.ArgumentParser(description='Phabricator integration.') 1985 parser = argparse.ArgumentParser(description='Phabricator integration.')
@@ -1989,6 +2029,12 @@ if __name__ == '__main__':
1989 help="commit or revision range to attach. When not specified, " 2029 help="commit or revision range to attach. When not specified, "
1990 "the tracking branch is used") \ 2030 "the tracking branch is used") \
1991 .completer = DisabledCompleter 2031 .completer = DisabledCompleter
2032 attach_parser.add_argument(
2033 '--autostash', action="store_true",
2034 help="Automatically stash not committed changes."
2035 " You can also `git config [--global] phab.autostash true` "
2036 "to make it permanent") \
2037 .completer = DisabledCompleter
1992 2038
1993 apply_parser = subparsers.add_parser( 2039 apply_parser = subparsers.add_parser(
1994 'apply', help="Apply a revision and its dependencies" 2040 'apply', help="Apply a revision and its dependencies"
@@ -2005,6 +2051,12 @@ if __name__ == '__main__':
2005 '--no-dependencies', "-n", action="store_true", 2051 '--no-dependencies', "-n", action="store_true",
2006 help="Do not apply dependencies of a revision.") \ 2052 help="Do not apply dependencies of a revision.") \
2007 .completer = DisabledCompleter 2053 .completer = DisabledCompleter
2054 apply_parser.add_argument(
2055 '--autostash', action="store_true",
2056 help="Automatically stash not committed changes."
2057 " You can also `git config [--global] phab.autostash true` "
2058 "to make it always happen") \
2059 .completer = DisabledCompleter
2008 2060
2009 log_parser = subparsers.add_parser( 2061 log_parser = subparsers.add_parser(
2010 'log', help="Show commit logs with their differential ID") 2062 'log', help="Show commit logs with their differential ID")
@@ -2050,6 +2102,12 @@ if __name__ == '__main__':
2050 'task', metavar='<T123>', nargs='?', 2102 'task', metavar='<T123>', nargs='?',
2051 help="The task to land") \ 2103 help="The task to land") \
2052 .completer = DisabledCompleter 2104 .completer = DisabledCompleter
2105 land_parser.add_argument(
2106 '--autostash', action="store_true",
2107 help="Automatically stash not committed changes."
2108 " You can also `git config [--global] phab.autostash true` "
2109 "to make it always happen") \
2110 .completer = DisabledCompleter
2053 2111
2054 argcomplete.autocomplete(parser) 2112 argcomplete.autocomplete(parser)
2055 2113