summaryrefslogtreecommitdiffstats
path: root/subcmds/rebase.py
diff options
context:
space:
mode:
Diffstat (limited to 'subcmds/rebase.py')
-rw-r--r--subcmds/rebase.py313
1 files changed, 180 insertions, 133 deletions
diff --git a/subcmds/rebase.py b/subcmds/rebase.py
index 3d1a63e4..dc4f5805 100644
--- a/subcmds/rebase.py
+++ b/subcmds/rebase.py
@@ -20,146 +20,193 @@ from git_command import GitCommand
20 20
21 21
22class RebaseColoring(Coloring): 22class RebaseColoring(Coloring):
23 def __init__(self, config): 23 def __init__(self, config):
24 Coloring.__init__(self, config, 'rebase') 24 Coloring.__init__(self, config, "rebase")
25 self.project = self.printer('project', attr='bold') 25 self.project = self.printer("project", attr="bold")
26 self.fail = self.printer('fail', fg='red') 26 self.fail = self.printer("fail", fg="red")
27 27
28 28
29class Rebase(Command): 29class Rebase(Command):
30 COMMON = True 30 COMMON = True
31 helpSummary = "Rebase local branches on upstream branch" 31 helpSummary = "Rebase local branches on upstream branch"
32 helpUsage = """ 32 helpUsage = """
33%prog {[<project>...] | -i <project>...} 33%prog {[<project>...] | -i <project>...}
34""" 34"""
35 helpDescription = """ 35 helpDescription = """
36'%prog' uses git rebase to move local changes in the current topic branch to 36'%prog' uses git rebase to move local changes in the current topic branch to
37the HEAD of the upstream history, useful when you have made commits in a topic 37the HEAD of the upstream history, useful when you have made commits in a topic
38branch but need to incorporate new upstream changes "underneath" them. 38branch but need to incorporate new upstream changes "underneath" them.
39""" 39"""
40 40
41 def _Options(self, p): 41 def _Options(self, p):
42 g = p.get_option_group('--quiet') 42 g = p.get_option_group("--quiet")
43 g.add_option('-i', '--interactive', 43 g.add_option(
44 dest="interactive", action="store_true", 44 "-i",
45 help="interactive rebase (single project only)") 45 "--interactive",
46 46 dest="interactive",
47 p.add_option('--fail-fast', 47 action="store_true",
48 dest='fail_fast', action='store_true', 48 help="interactive rebase (single project only)",
49 help='stop rebasing after first error is hit') 49 )
50 p.add_option('-f', '--force-rebase', 50
51 dest='force_rebase', action='store_true', 51 p.add_option(
52 help='pass --force-rebase to git rebase') 52 "--fail-fast",
53 p.add_option('--no-ff', 53 dest="fail_fast",
54 dest='ff', default=True, action='store_false', 54 action="store_true",
55 help='pass --no-ff to git rebase') 55 help="stop rebasing after first error is hit",
56 p.add_option('--autosquash', 56 )
57 dest='autosquash', action='store_true', 57 p.add_option(
58 help='pass --autosquash to git rebase') 58 "-f",
59 p.add_option('--whitespace', 59 "--force-rebase",
60 dest='whitespace', action='store', metavar='WS', 60 dest="force_rebase",
61 help='pass --whitespace to git rebase') 61 action="store_true",
62 p.add_option('--auto-stash', 62 help="pass --force-rebase to git rebase",
63 dest='auto_stash', action='store_true', 63 )
64 help='stash local modifications before starting') 64 p.add_option(
65 p.add_option('-m', '--onto-manifest', 65 "--no-ff",
66 dest='onto_manifest', action='store_true', 66 dest="ff",
67 help='rebase onto the manifest version instead of upstream ' 67 default=True,
68 'HEAD (this helps to make sure the local tree stays ' 68 action="store_false",
69 'consistent if you previously synced to a manifest)') 69 help="pass --no-ff to git rebase",
70 70 )
71 def Execute(self, opt, args): 71 p.add_option(
72 all_projects = self.GetProjects(args, all_manifests=not opt.this_manifest_only) 72 "--autosquash",
73 one_project = len(all_projects) == 1 73 dest="autosquash",
74 74 action="store_true",
75 if opt.interactive and not one_project: 75 help="pass --autosquash to git rebase",
76 print('error: interactive rebase not supported with multiple projects', 76 )
77 file=sys.stderr) 77 p.add_option(
78 if len(args) == 1: 78 "--whitespace",
79 print('note: project %s is mapped to more than one path' % (args[0],), 79 dest="whitespace",
80 file=sys.stderr) 80 action="store",
81 return 1 81 metavar="WS",
82 82 help="pass --whitespace to git rebase",
83 # Setup the common git rebase args that we use for all projects. 83 )
84 common_args = ['rebase'] 84 p.add_option(
85 if opt.whitespace: 85 "--auto-stash",
86 common_args.append('--whitespace=%s' % opt.whitespace) 86 dest="auto_stash",
87 if opt.quiet: 87 action="store_true",
88 common_args.append('--quiet') 88 help="stash local modifications before starting",
89 if opt.force_rebase: 89 )
90 common_args.append('--force-rebase') 90 p.add_option(
91 if not opt.ff: 91 "-m",
92 common_args.append('--no-ff') 92 "--onto-manifest",
93 if opt.autosquash: 93 dest="onto_manifest",
94 common_args.append('--autosquash') 94 action="store_true",
95 if opt.interactive: 95 help="rebase onto the manifest version instead of upstream "
96 common_args.append('-i') 96 "HEAD (this helps to make sure the local tree stays "
97 97 "consistent if you previously synced to a manifest)",
98 config = self.manifest.manifestProject.config 98 )
99 out = RebaseColoring(config) 99
100 out.redirect(sys.stdout) 100 def Execute(self, opt, args):
101 _RelPath = lambda p: p.RelPath(local=opt.this_manifest_only) 101 all_projects = self.GetProjects(
102 102 args, all_manifests=not opt.this_manifest_only
103 ret = 0 103 )
104 for project in all_projects: 104 one_project = len(all_projects) == 1
105 if ret and opt.fail_fast: 105
106 break 106 if opt.interactive and not one_project:
107 107 print(
108 cb = project.CurrentBranch 108 "error: interactive rebase not supported with multiple "
109 if not cb: 109 "projects",
110 if one_project: 110 file=sys.stderr,
111 print("error: project %s has a detached HEAD" % _RelPath(project), 111 )
112 file=sys.stderr) 112 if len(args) == 1:
113 return 1 113 print(
114 # ignore branches with detatched HEADs 114 "note: project %s is mapped to more than one path"
115 continue 115 % (args[0],),
116 116 file=sys.stderr,
117 upbranch = project.GetBranch(cb) 117 )
118 if not upbranch.LocalMerge: 118 return 1
119 if one_project: 119
120 print("error: project %s does not track any remote branches" 120 # Setup the common git rebase args that we use for all projects.
121 % _RelPath(project), file=sys.stderr) 121 common_args = ["rebase"]
122 return 1 122 if opt.whitespace:
123 # ignore branches without remotes 123 common_args.append("--whitespace=%s" % opt.whitespace)
124 continue 124 if opt.quiet:
125 125 common_args.append("--quiet")
126 args = common_args[:] 126 if opt.force_rebase:
127 if opt.onto_manifest: 127 common_args.append("--force-rebase")
128 args.append('--onto') 128 if not opt.ff:
129 args.append(project.revisionExpr) 129 common_args.append("--no-ff")
130 130 if opt.autosquash:
131 args.append(upbranch.LocalMerge) 131 common_args.append("--autosquash")
132 132 if opt.interactive:
133 out.project('project %s: rebasing %s -> %s', 133 common_args.append("-i")
134 _RelPath(project), cb, upbranch.LocalMerge) 134
135 out.nl() 135 config = self.manifest.manifestProject.config
136 out.flush() 136 out = RebaseColoring(config)
137 137 out.redirect(sys.stdout)
138 needs_stash = False 138 _RelPath = lambda p: p.RelPath(local=opt.this_manifest_only)
139 if opt.auto_stash: 139
140 stash_args = ["update-index", "--refresh", "-q"] 140 ret = 0
141 141 for project in all_projects:
142 if GitCommand(project, stash_args).Wait() != 0: 142 if ret and opt.fail_fast:
143 needs_stash = True 143 break
144 # Dirty index, requires stash... 144
145 stash_args = ["stash"] 145 cb = project.CurrentBranch
146 146 if not cb:
147 if GitCommand(project, stash_args).Wait() != 0: 147 if one_project:
148 ret += 1 148 print(
149 continue 149 "error: project %s has a detached HEAD"
150 150 % _RelPath(project),
151 if GitCommand(project, args).Wait() != 0: 151 file=sys.stderr,
152 ret += 1 152 )
153 continue 153 return 1
154 154 # Ignore branches with detached HEADs.
155 if needs_stash: 155 continue
156 stash_args.append('pop') 156
157 stash_args.append('--quiet') 157 upbranch = project.GetBranch(cb)
158 if GitCommand(project, stash_args).Wait() != 0: 158 if not upbranch.LocalMerge:
159 ret += 1 159 if one_project:
160 160 print(
161 if ret: 161 "error: project %s does not track any remote branches"
162 out.fail('%i projects had errors', ret) 162 % _RelPath(project),
163 out.nl() 163 file=sys.stderr,
164 164 )
165 return ret 165 return 1
166 # Ignore branches without remotes.
167 continue
168
169 args = common_args[:]
170 if opt.onto_manifest:
171 args.append("--onto")
172 args.append(project.revisionExpr)
173
174 args.append(upbranch.LocalMerge)
175
176 out.project(
177 "project %s: rebasing %s -> %s",
178 _RelPath(project),
179 cb,
180 upbranch.LocalMerge,
181 )
182 out.nl()
183 out.flush()
184
185 needs_stash = False
186 if opt.auto_stash:
187 stash_args = ["update-index", "--refresh", "-q"]
188
189 if GitCommand(project, stash_args).Wait() != 0:
190 needs_stash = True
191 # Dirty index, requires stash...
192 stash_args = ["stash"]
193
194 if GitCommand(project, stash_args).Wait() != 0:
195 ret += 1
196 continue
197
198 if GitCommand(project, args).Wait() != 0:
199 ret += 1
200 continue
201
202 if needs_stash:
203 stash_args.append("pop")
204 stash_args.append("--quiet")
205 if GitCommand(project, stash_args).Wait() != 0:
206 ret += 1
207
208 if ret:
209 out.fail("%i projects had errors", ret)
210 out.nl()
211
212 return ret