From 9bb1816bdc2c21811ea6a87ba6eb745bdf3c041c Mon Sep 17 00:00:00 2001 From: Nico Sallembien Date: Mon, 7 Dec 2009 15:38:01 -0800 Subject: [PATCH 1/6] Fixing project renaming bug. This bug happens when a project gets added to the manifest, and then is renamed. Users who happened to have run "repo sync" after the project was added but before the rename happened will try to read the data from the old project, as the manifest was only updated after all projects were updated successfully. --- subcmds/sync.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/subcmds/sync.py b/subcmds/sync.py index bd07dd9f..ceb81eaa 100644 --- a/subcmds/sync.py +++ b/subcmds/sync.py @@ -111,7 +111,6 @@ later is required to fix a server side protocol bug. pm = Progress('Fetching projects', len(projects)) for project in projects: pm.update() - if project.Sync_NetworkHalf(): fetched.add(project.gitdir) else: @@ -192,6 +191,15 @@ uncommitted changes are present' % project.relpath if opt.repo_upgraded: _PostRepoUpgrade(self.manifest) + if not opt.local_only: + mp.Sync_NetworkHalf() + + if mp.HasChanges: + syncbuf = SyncBuffer(mp.config) + mp.Sync_LocalHalf(syncbuf) + if not syncbuf.Finish(): + sys.exit(1) + self.manifest._Unload() all = self.GetProjects(args, missing_ok=True) if not opt.local_only: @@ -199,7 +207,6 @@ uncommitted changes are present' % project.relpath now = time.time() if (24 * 60 * 60) <= (now - rp.LastFetch): to_fetch.append(rp) - to_fetch.append(mp) to_fetch.extend(all) fetched = self._Fetch(to_fetch) @@ -208,12 +215,6 @@ uncommitted changes are present' % project.relpath # bail out now; the rest touches the working tree return - if mp.HasChanges: - syncbuf = SyncBuffer(mp.config) - mp.Sync_LocalHalf(syncbuf) - if not syncbuf.Finish(): - sys.exit(1) - self.manifest._Unload() all = self.GetProjects(args, missing_ok=True) missing = [] @@ -241,7 +242,6 @@ uncommitted changes are present' % project.relpath if not syncbuf.Finish(): sys.exit(1) - def _PostRepoUpgrade(manifest): for project in manifest.projects.values(): if project.Exists: From aa4982e4c937d9be0f69c250692839eb98a184e8 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 30 Dec 2009 18:38:27 -0800 Subject: [PATCH 2/6] sync: Fix split call on malformed email addresses If an email address in a commit object contains a space, like a few malformed ones on the Linux kernel, we still want to split only on the first space. Unfortunately my brain was too damaged by Perl and originally wrote the split asking for 2 results; in Python split's argument is how many splits to perform. Here we want only 1 split, to break apart the commit identity from the email address on the same line. Signed-off-by: Shawn O. Pearce --- project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.py b/project.py index 1beee2a6..4930a275 100644 --- a/project.py +++ b/project.py @@ -728,7 +728,7 @@ class Project(object): last_mine = None cnt_mine = 0 for commit in local_changes: - commit_id, committer_email = commit.split(' ', 2) + commit_id, committer_email = commit.split(' ', 1) if committer_email == self.UserEmail: last_mine = commit_id cnt_mine += 1 From b6ea3bfcc398417b91a4fa5a486324f4d904b022 Mon Sep 17 00:00:00 2001 From: Ulrik Sjolin Date: Sun, 3 Jan 2010 18:20:17 +0100 Subject: [PATCH 3/6] Honor url.insteadOf when setting up SSH control master connection Repo can now properly handle url.insteadOf sections in the user's ~/.gitconfig file. This means that a user can now enjoy the master-ssh functionality even if he/she uses insteadOf's in ~/.gitconfig to rewrite git:// URLs to ssh:// style URLs. Change-Id: Ic0f04a9c57206a7b89eb0f10bf188c4c483debe3 Signed-off-by: Shawn O. Pearce --- git_config.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/git_config.py b/git_config.py index e1e20463..45a2d257 100644 --- a/git_config.py +++ b/git_config.py @@ -442,8 +442,30 @@ class Remote(object): self._Get('fetch', all=True)) self._review_protocol = None + def _InsteadOf(self): + globCfg = GitConfig.ForUser() + urlList = globCfg.GetSubSections('url') + longest = "" + longestUrl = "" + + for url in urlList: + key = "url." + url + ".insteadOf" + insteadOfList = globCfg.GetString(key, all=True) + + for insteadOf in insteadOfList: + if self.url.startswith(insteadOf) \ + and len(insteadOf) > len(longest): + longest = insteadOf + longestUrl = url + + if len(longest) == 0: + return self.url + + return self.url.replace(longest, longestUrl, 1) + def PreConnectFetch(self): - return _preconnect(self.url) + connectionUrl = self._InsteadOf() + return _preconnect(connectionUrl) @property def ReviewProtocol(self): From d63060fc9546e2132d0ad7791beb795906372e86 Mon Sep 17 00:00:00 2001 From: Nico Sallembien Date: Wed, 20 Jan 2010 10:27:50 -0800 Subject: [PATCH 4/6] Check that we are not overwriting a local repository when syncing. If a local git repository exists within the same folder as a new project that is added, when the user syncs the repo, the sync will overwrite the local files under the project's .git repository with its own symlinks. Make sure that we do not overwrite 'normal' files in repo and throw an error when that happens. --- project.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/project.py b/project.py index 4930a275..902a2b44 100644 --- a/project.py +++ b/project.py @@ -1120,7 +1120,10 @@ class Project(object): try: src = os.path.join(self.gitdir, name) dst = os.path.join(dotgit, name) - os.symlink(relpath(src, dst), dst) + if os.path.islink(dst) or not os.path.exists(dst): + os.symlink(relpath(src, dst), dst) + else: + raise GitError('cannot overwrite a local work tree') except OSError, e: if e.errno == errno.EPERM: raise GitError('filesystem must support symlinks') From 4c50deea28badb7007fa6b78c187de50eacdd07a Mon Sep 17 00:00:00 2001 From: Daniel Sandler Date: Tue, 2 Mar 2010 15:38:03 -0500 Subject: [PATCH 5/6] Fail sync when encountering "N commits behind." This is almost always something the user needs to address before continuing work, so promoting it to a failure (rather than simply an informational message) seems the right way to go. As a side-effect, repo will now exit with a non-zero status code in this situation, so pipelines of the form `repo sync && make` will fail if there are branches that are stalled due to uploaded but unmerged patches. --- project.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/project.py b/project.py index 902a2b44..3d1783f8 100644 --- a/project.py +++ b/project.py @@ -706,10 +706,9 @@ class Project(object): # commits are not yet merged upstream. We do not want # to rewrite the published commits so we punt. # - syncbuf.info(self, - "branch %s is published but is now %d commits behind", - branch.name, - len(upstream_gain)) + syncbuf.fail(self, + "branch %s is published (but not merged) and is now %d commits behind" + % (branch.name, len(upstream_gain))) return elif pub == head: # All published commits are merged, and thus we are a From 9452e4ec0941fbee163e35ebdcd6ca6ee7df55cb Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 22 Aug 2009 18:17:46 -0700 Subject: [PATCH 6/6] Automatically install Gerrit Code Review's commit-msg hook Most users of repo are also using Gerrit Code Review, and will want the commit-msg hook to be automatically installed into their local projects so that Change-Ids are assigned when commits are created, not when they are first uploaded. (cherry picked from commit a949fa5d202f0a1f812d7630f3e5bf0f02ca4e98 but squashed with latest hook script from version 2.1.2) Change-Id: Ie68b2d60ac85d8c2285d2e1e6a4536eb76695547 Signed-off-by: Shawn O. Pearce --- hooks/commit-msg | 101 +++++++++++++++++++++++++++++++++++++++++++++++ project.py | 22 +++++++++-- 2 files changed, 119 insertions(+), 4 deletions(-) create mode 100755 hooks/commit-msg diff --git a/hooks/commit-msg b/hooks/commit-msg new file mode 100755 index 00000000..712921c9 --- /dev/null +++ b/hooks/commit-msg @@ -0,0 +1,101 @@ +#!/bin/sh +# From Gerrit Code Review 2.1.2-rc2-33-g7e30c72 +# +# Part of Gerrit Code Review (http://code.google.com/p/gerrit/) +# +# Copyright (C) 2009 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. +# + +CHANGE_ID_AFTER="Bug|Issue" +MSG="$1" + +# Check for, and add if missing, a unique Change-Id +# +add_ChangeId() { + clean_message=$(sed -e ' + /^diff --git a\/.*/{ + s/// + q + } + /^Signed-off-by:/d + /^#/d + ' "$MSG" | git stripspace) + if test -z "$clean_message" + then + return + fi + + if grep -i '^Change-Id:' "$MSG" >/dev/null + then + return + fi + + id=$(_gen_ChangeId) + perl -e ' + $MSG = shift; + $id = shift; + $CHANGE_ID_AFTER = shift; + + undef $/; + open(I, $MSG); $_ = ; close I; + s|^diff --git a/.*||ms; + s|^#.*$||mg; + exit unless $_; + + @message = split /\n/; + $haveFooter = 0; + $startFooter = @message; + for($line = @message - 1; $line >= 0; $line--) { + $_ = $message[$line]; + + ($haveFooter++, next) if /^[a-zA-Z0-9-]+:/; + next if /^[ []/; + $startFooter = $line if ($haveFooter && /^\r?$/); + last; + } + + @footer = @message[$startFooter+1..@message]; + @message = @message[0..$startFooter]; + push(@footer, "") unless @footer; + + for ($line = 0; $line < @footer; $line++) { + $_ = $footer[$line]; + next if /^($CHANGE_ID_AFTER):/i; + last; + } + splice(@footer, $line, 0, "Change-Id: I$id"); + + $_ = join("\n", @message, @footer); + open(O, ">$MSG"); print O; close O; + ' "$MSG" "$id" "$CHANGE_ID_AFTER" +} +_gen_ChangeIdInput() { + echo "tree $(git write-tree)" + if parent=$(git rev-parse HEAD^0 2>/dev/null) + then + echo "parent $parent" + fi + echo "author $(git var GIT_AUTHOR_IDENT)" + echo "committer $(git var GIT_COMMITTER_IDENT)" + echo + printf '%s' "$clean_message" +} +_gen_ChangeId() { + _gen_ChangeIdInput | + git hash-object -t commit --stdin +} + + +add_ChangeId diff --git a/project.py b/project.py index 3d1783f8..ff896d01 100644 --- a/project.py +++ b/project.py @@ -1055,13 +1055,27 @@ class Project(object): if not os.path.exists(hooks): os.makedirs(hooks) for stock_hook in repo_hooks(): - dst = os.path.join(hooks, os.path.basename(stock_hook)) + name = os.path.basename(stock_hook) + + if name in ('commit-msg') and not self.remote.review: + # Don't install a Gerrit Code Review hook if this + # project does not appear to use it for reviews. + # + continue + + dst = os.path.join(hooks, name) + if os.path.islink(dst): + continue + if os.path.exists(dst): + if filecmp.cmp(stock_hook, dst, shallow=False): + os.remove(dst) + else: + _error("%s: Not replacing %s hook", self.relpath, name) + continue try: os.symlink(relpath(stock_hook, dst), dst) except OSError, e: - if e.errno == errno.EEXIST: - pass - elif e.errno == errno.EPERM: + if e.errno == errno.EPERM: raise GitError('filesystem must support symlinks') else: raise