mirror of
https://gerrit.googlesource.com/git-repo
synced 2025-06-26 20:17:52 +00:00
Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
30d452905f | |||
d6c93a28ca | |||
d572a13021 | |||
3ba5f95b46 | |||
2630dd9787 | |||
dafb1d68d3 | |||
4655e81a75 | |||
723c5dc3d6 |
48
project.py
48
project.py
@ -650,13 +650,18 @@ class Project(object):
|
||||
|
||||
return False
|
||||
|
||||
def PrintWorkTreeStatus(self):
|
||||
def PrintWorkTreeStatus(self, output_redir=None):
|
||||
"""Prints the status of the repository to stdout.
|
||||
|
||||
Args:
|
||||
output: If specified, redirect the output to this object.
|
||||
"""
|
||||
if not os.path.isdir(self.worktree):
|
||||
print ''
|
||||
print 'project %s/' % self.relpath
|
||||
print ' missing (run "repo sync")'
|
||||
if output_redir == None:
|
||||
output_redir = sys.stdout
|
||||
print >>output_redir, ''
|
||||
print >>output_redir, 'project %s/' % self.relpath
|
||||
print >>output_redir, ' missing (run "repo sync")'
|
||||
return
|
||||
|
||||
self.work_git.update_index('-q',
|
||||
@ -671,6 +676,8 @@ class Project(object):
|
||||
return 'CLEAN'
|
||||
|
||||
out = StatusColoring(self.config)
|
||||
if not output_redir == None:
|
||||
out.redirect(output_redir)
|
||||
out.project('project %-40s', self.relpath + '/')
|
||||
|
||||
branch = self.CurrentBranch
|
||||
@ -720,6 +727,7 @@ class Project(object):
|
||||
else:
|
||||
out.write('%s', line)
|
||||
out.nl()
|
||||
|
||||
return 'DIRTY'
|
||||
|
||||
def PrintWorkTreeDiff(self):
|
||||
@ -783,7 +791,7 @@ class Project(object):
|
||||
if R_HEADS + n not in heads:
|
||||
self.bare_git.DeleteRef(name, id)
|
||||
|
||||
def GetUploadableBranches(self):
|
||||
def GetUploadableBranches(self, selected_branch=None):
|
||||
"""List any branches which can be uploaded for review.
|
||||
"""
|
||||
heads = {}
|
||||
@ -799,6 +807,8 @@ class Project(object):
|
||||
for branch, id in heads.iteritems():
|
||||
if branch in pubed and pubed[branch] == id:
|
||||
continue
|
||||
if selected_branch and branch != selected_branch:
|
||||
continue
|
||||
|
||||
rb = self.GetUploadableBranch(branch)
|
||||
if rb:
|
||||
@ -1159,6 +1169,13 @@ class Project(object):
|
||||
|
||||
def CheckoutBranch(self, name):
|
||||
"""Checkout a local topic branch.
|
||||
|
||||
Args:
|
||||
name: The name of the branch to checkout.
|
||||
|
||||
Returns:
|
||||
True if the checkout succeeded; False if it didn't; None if the branch
|
||||
didn't exist.
|
||||
"""
|
||||
rev = R_HEADS + name
|
||||
head = self.work_git.GetHead()
|
||||
@ -1173,7 +1190,7 @@ class Project(object):
|
||||
except KeyError:
|
||||
# Branch does not exist in this project
|
||||
#
|
||||
return False
|
||||
return None
|
||||
|
||||
if head.startswith(R_HEADS):
|
||||
try:
|
||||
@ -1196,13 +1213,19 @@ class Project(object):
|
||||
|
||||
def AbandonBranch(self, name):
|
||||
"""Destroy a local topic branch.
|
||||
|
||||
Args:
|
||||
name: The name of the branch to abandon.
|
||||
|
||||
Returns:
|
||||
True if the abandon succeeded; False if it didn't; None if the branch
|
||||
didn't exist.
|
||||
"""
|
||||
rev = R_HEADS + name
|
||||
all = self.bare_ref.all
|
||||
if rev not in all:
|
||||
# Doesn't exist; assume already abandoned.
|
||||
#
|
||||
return True
|
||||
# Doesn't exist
|
||||
return None
|
||||
|
||||
head = self.work_git.GetHead()
|
||||
if head == rev:
|
||||
@ -1347,6 +1370,13 @@ class Project(object):
|
||||
ref_dir = None
|
||||
|
||||
cmd = ['fetch']
|
||||
|
||||
# The --depth option only affects the initial fetch; after that we'll do
|
||||
# full fetches of changes.
|
||||
depth = self.manifest.manifestProject.config.GetString('repo.depth')
|
||||
if depth and initial:
|
||||
cmd.append('--depth=%s' % depth)
|
||||
|
||||
if quiet:
|
||||
cmd.append('--quiet')
|
||||
if not self.worktree:
|
||||
|
@ -41,21 +41,30 @@ It is equivalent to "git branch -D <branchname>".
|
||||
|
||||
nb = args[0]
|
||||
err = []
|
||||
success = []
|
||||
all = self.GetProjects(args[1:])
|
||||
|
||||
pm = Progress('Abandon %s' % nb, len(all))
|
||||
for project in all:
|
||||
pm.update()
|
||||
if not project.AbandonBranch(nb):
|
||||
err.append(project)
|
||||
|
||||
status = project.AbandonBranch(nb)
|
||||
if status is not None:
|
||||
if status:
|
||||
success.append(project)
|
||||
else:
|
||||
err.append(project)
|
||||
pm.end()
|
||||
|
||||
if err:
|
||||
if len(err) == len(all):
|
||||
print >>sys.stderr, 'error: no project has branch %s' % nb
|
||||
else:
|
||||
for p in err:
|
||||
print >>sys.stderr,\
|
||||
"error: %s/: cannot abandon %s" \
|
||||
% (p.relpath, nb)
|
||||
for p in err:
|
||||
print >>sys.stderr,\
|
||||
"error: %s/: cannot abandon %s" \
|
||||
% (p.relpath, nb)
|
||||
sys.exit(1)
|
||||
elif not success:
|
||||
print >>sys.stderr, 'error: no project has branch %s' % nb
|
||||
sys.exit(1)
|
||||
else:
|
||||
print >>sys.stderr, 'Abandoned in %d project(s):\n %s' % (
|
||||
len(success), '\n '.join(p.relpath for p in success))
|
||||
|
@ -38,21 +38,27 @@ The command is equivalent to:
|
||||
|
||||
nb = args[0]
|
||||
err = []
|
||||
success = []
|
||||
all = self.GetProjects(args[1:])
|
||||
|
||||
pm = Progress('Checkout %s' % nb, len(all))
|
||||
for project in all:
|
||||
pm.update()
|
||||
if not project.CheckoutBranch(nb):
|
||||
err.append(project)
|
||||
|
||||
status = project.CheckoutBranch(nb)
|
||||
if status is not None:
|
||||
if status:
|
||||
success.append(project)
|
||||
else:
|
||||
err.append(project)
|
||||
pm.end()
|
||||
|
||||
if err:
|
||||
if len(err) == len(all):
|
||||
print >>sys.stderr, 'error: no project has branch %s' % nb
|
||||
else:
|
||||
for p in err:
|
||||
print >>sys.stderr,\
|
||||
"error: %s/: cannot checkout %s" \
|
||||
% (p.relpath, nb)
|
||||
for p in err:
|
||||
print >>sys.stderr,\
|
||||
"error: %s/: cannot checkout %s" \
|
||||
% (p.relpath, nb)
|
||||
sys.exit(1)
|
||||
elif not success:
|
||||
print >>sys.stderr, 'error: no project has branch %s' % nb
|
||||
sys.exit(1)
|
||||
|
114
subcmds/cherry_pick.py
Normal file
114
subcmds/cherry_pick.py
Normal file
@ -0,0 +1,114 @@
|
||||
#
|
||||
# Copyright (C) 2010 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import sys, re, string, random, os
|
||||
from command import Command
|
||||
from git_command import GitCommand
|
||||
|
||||
CHANGE_ID_RE = re.compile(r'^\s*Change-Id: I([0-9a-f]{40})\s*$')
|
||||
|
||||
class CherryPick(Command):
|
||||
common = True
|
||||
helpSummary = "Cherry-pick a change."
|
||||
helpUsage = """
|
||||
%prog <sha1>
|
||||
"""
|
||||
helpDescription = """
|
||||
'%prog' cherry-picks a change from one branch to another.
|
||||
The change id will be updated, and a reference to the old
|
||||
change id will be added.
|
||||
"""
|
||||
|
||||
def _Options(self, p):
|
||||
pass
|
||||
|
||||
def Execute(self, opt, args):
|
||||
if len(args) != 1:
|
||||
self.Usage()
|
||||
|
||||
reference = args[0]
|
||||
|
||||
p = GitCommand(None,
|
||||
['rev-parse', '--verify', reference],
|
||||
capture_stdout = True,
|
||||
capture_stderr = True)
|
||||
if p.Wait() != 0:
|
||||
print >>sys.stderr, p.stderr
|
||||
sys.exit(1)
|
||||
sha1 = p.stdout.strip()
|
||||
|
||||
p = GitCommand(None, ['cat-file', 'commit', sha1], capture_stdout=True)
|
||||
if p.Wait() != 0:
|
||||
print >>sys.stderr, "error: Failed to retrieve old commit message"
|
||||
sys.exit(1)
|
||||
old_msg = self._StripHeader(p.stdout)
|
||||
|
||||
p = GitCommand(None,
|
||||
['cherry-pick', sha1],
|
||||
capture_stdout = True,
|
||||
capture_stderr = True)
|
||||
status = p.Wait()
|
||||
|
||||
print >>sys.stdout, p.stdout
|
||||
print >>sys.stderr, p.stderr
|
||||
|
||||
if status == 0:
|
||||
# The cherry-pick was applied correctly. We just need to edit the
|
||||
# commit message.
|
||||
new_msg = self._Reformat(old_msg, sha1)
|
||||
|
||||
p = GitCommand(None, ['commit', '--amend', '-F', '-'],
|
||||
provide_stdin = True,
|
||||
capture_stdout = True,
|
||||
capture_stderr = True)
|
||||
p.stdin.write(new_msg)
|
||||
if p.Wait() != 0:
|
||||
print >>sys.stderr, "error: Failed to update commit message"
|
||||
sys.exit(1)
|
||||
|
||||
else:
|
||||
print >>sys.stderr, """\
|
||||
NOTE: When committing (please see above) and editing the commit message,
|
||||
please remove the old Change-Id-line and add:
|
||||
"""
|
||||
print >>sys.stderr, self._GetReference(sha1)
|
||||
print >>sys.stderr
|
||||
|
||||
def _IsChangeId(self, line):
|
||||
return CHANGE_ID_RE.match(line)
|
||||
|
||||
def _GetReference(self, sha1):
|
||||
return "(cherry picked from commit %s)" % sha1
|
||||
|
||||
def _StripHeader(self, commit_msg):
|
||||
lines = commit_msg.splitlines()
|
||||
return "\n".join(lines[lines.index("")+1:])
|
||||
|
||||
def _Reformat(self, old_msg, sha1):
|
||||
new_msg = []
|
||||
|
||||
for line in old_msg.splitlines():
|
||||
if not self._IsChangeId(line):
|
||||
new_msg.append(line)
|
||||
|
||||
# Add a blank line between the message and the change id/reference
|
||||
try:
|
||||
if new_msg[-1].strip() != "":
|
||||
new_msg.append("")
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
new_msg.append(self._GetReference(sha1))
|
||||
return "\n".join(new_msg)
|
@ -14,6 +14,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
from color import Coloring
|
||||
@ -81,6 +82,9 @@ to update the working directory files.
|
||||
g.add_option('--reference',
|
||||
dest='reference',
|
||||
help='location of mirror directory', metavar='DIR')
|
||||
g.add_option('--depth', type='int', default=None,
|
||||
dest='depth',
|
||||
help='create a shallow clone with given depth; see git clone')
|
||||
|
||||
# Tool
|
||||
g = p.add_option_group('repo Version options')
|
||||
@ -137,6 +141,11 @@ to update the working directory files.
|
||||
if not m.Sync_NetworkHalf():
|
||||
r = m.GetRemote(m.remote.name)
|
||||
print >>sys.stderr, 'fatal: cannot obtain manifest %s' % r.url
|
||||
|
||||
# Better delete the manifest git dir if we created it; otherwise next
|
||||
# time (when user fixes problems) we won't go through the "is_new" logic.
|
||||
if is_new:
|
||||
shutil.rmtree(m.gitdir)
|
||||
sys.exit(1)
|
||||
|
||||
syncbuf = SyncBuffer(m.config)
|
||||
@ -226,6 +235,25 @@ to update the working directory files.
|
||||
if a in ('y', 'yes', 't', 'true', 'on'):
|
||||
gc.SetString('color.ui', 'auto')
|
||||
|
||||
def _ConfigureDepth(self, opt):
|
||||
"""Configure the depth we'll sync down.
|
||||
|
||||
Args:
|
||||
opt: Options from optparse. We care about opt.depth.
|
||||
"""
|
||||
# Opt.depth will be non-None if user actually passed --depth to repo init.
|
||||
if opt.depth is not None:
|
||||
if opt.depth > 0:
|
||||
# Positive values will set the depth.
|
||||
depth = str(opt.depth)
|
||||
else:
|
||||
# Negative numbers will clear the depth; passing None to SetString
|
||||
# will do that.
|
||||
depth = None
|
||||
|
||||
# We store the depth in the main manifest project.
|
||||
self.manifest.manifestProject.config.SetString('repo.depth', depth)
|
||||
|
||||
def Execute(self, opt, args):
|
||||
git_require(MIN_GIT_VERSION, fail=True)
|
||||
self._SyncManifest(opt)
|
||||
@ -235,6 +263,8 @@ to update the working directory files.
|
||||
self._ConfigureUser()
|
||||
self._ConfigureColor()
|
||||
|
||||
self._ConfigureDepth(opt)
|
||||
|
||||
if self.manifest.IsMirror:
|
||||
type = 'mirror '
|
||||
else:
|
||||
|
@ -15,6 +15,15 @@
|
||||
|
||||
from command import PagedCommand
|
||||
|
||||
try:
|
||||
import threading as _threading
|
||||
except ImportError:
|
||||
import dummy_threading as _threading
|
||||
|
||||
import itertools
|
||||
import sys
|
||||
import StringIO
|
||||
|
||||
class Status(PagedCommand):
|
||||
common = True
|
||||
helpSummary = "Show the working tree status"
|
||||
@ -27,6 +36,9 @@ and the most recent commit on this branch (HEAD), in each project
|
||||
specified. A summary is displayed, one line per file where there
|
||||
is a difference between these three states.
|
||||
|
||||
The -j/--jobs option can be used to run multiple status queries
|
||||
in parallel.
|
||||
|
||||
Status Display
|
||||
--------------
|
||||
|
||||
@ -60,9 +72,34 @@ the following meanings:
|
||||
|
||||
"""
|
||||
|
||||
def _Options(self, p):
|
||||
p.add_option('-j', '--jobs',
|
||||
dest='jobs', action='store', type='int', default=2,
|
||||
help="number of projects to check simultaneously")
|
||||
|
||||
def _StatusHelper(self, project, clean_counter, sem, output):
|
||||
"""Obtains the status for a specific project.
|
||||
|
||||
Obtains the status for a project, redirecting the output to
|
||||
the specified object. It will release the semaphore
|
||||
when done.
|
||||
|
||||
Args:
|
||||
project: Project to get status of.
|
||||
clean_counter: Counter for clean projects.
|
||||
sem: Semaphore, will call release() when complete.
|
||||
output: Where to output the status.
|
||||
"""
|
||||
try:
|
||||
state = project.PrintWorkTreeStatus(output)
|
||||
if state == 'CLEAN':
|
||||
clean_counter.next()
|
||||
finally:
|
||||
sem.release()
|
||||
|
||||
def Execute(self, opt, args):
|
||||
all = self.GetProjects(args)
|
||||
clean = 0
|
||||
counter = itertools.count()
|
||||
|
||||
on = {}
|
||||
for project in all:
|
||||
@ -77,9 +114,24 @@ the following meanings:
|
||||
for cb in branch_names:
|
||||
print '# on branch %s' % cb
|
||||
|
||||
for project in all:
|
||||
state = project.PrintWorkTreeStatus()
|
||||
if state == 'CLEAN':
|
||||
clean += 1
|
||||
if len(all) == clean:
|
||||
if opt.jobs == 1:
|
||||
for project in all:
|
||||
state = project.PrintWorkTreeStatus()
|
||||
if state == 'CLEAN':
|
||||
counter.next()
|
||||
else:
|
||||
sem = _threading.Semaphore(opt.jobs)
|
||||
threads_and_output = []
|
||||
for project in all:
|
||||
sem.acquire()
|
||||
output = StringIO.StringIO()
|
||||
t = _threading.Thread(target=self._StatusHelper,
|
||||
args=(project, counter, sem, output))
|
||||
threads_and_output.append((t, output))
|
||||
t.start()
|
||||
for (t, output) in threads_and_output:
|
||||
t.join()
|
||||
sys.stdout.write(output.getvalue())
|
||||
output.close()
|
||||
if len(all) == counter.next():
|
||||
print 'nothing to commit (working directory clean)'
|
||||
|
@ -218,7 +218,7 @@ later is required to fix a server side protocol bug.
|
||||
for project in projects:
|
||||
# Check for any errors before starting any new threads.
|
||||
# ...we'll let existing threads finish, though.
|
||||
if err_event.is_set():
|
||||
if err_event.isSet():
|
||||
break
|
||||
|
||||
sem.acquire()
|
||||
@ -237,7 +237,7 @@ later is required to fix a server side protocol bug.
|
||||
t.join()
|
||||
|
||||
# If we saw an error, exit with code 1 so that other scripts can check.
|
||||
if err_event.is_set():
|
||||
if err_event.isSet():
|
||||
print >>sys.stderr, '\nerror: Exited sync due to fetch errors'
|
||||
sys.exit(1)
|
||||
|
||||
|
@ -120,6 +120,9 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
|
||||
p.add_option('--cc',
|
||||
type='string', action='append', dest='cc',
|
||||
help='Also send email to these email addresses.')
|
||||
p.add_option('--br',
|
||||
type='string', action='store', dest='branch',
|
||||
help='Branch to upload.')
|
||||
|
||||
# Options relating to upload hook. Note that verify and no-verify are NOT
|
||||
# opposites of each other, which is why they store to different locations.
|
||||
@ -336,9 +339,13 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
|
||||
pending = []
|
||||
reviewers = []
|
||||
cc = []
|
||||
branch = None
|
||||
|
||||
if opt.branch:
|
||||
branch = opt.branch
|
||||
|
||||
for project in project_list:
|
||||
avail = project.GetUploadableBranches()
|
||||
avail = project.GetUploadableBranches(branch)
|
||||
if avail:
|
||||
pending.append((project, avail))
|
||||
|
||||
|
Reference in New Issue
Block a user