Commit 02b3c98b authored by agable's avatar agable Committed by Commit bot

Make git-freeze bail out if the user has too much untracked data.

R=iannucci@chromium.org
BUG=376099

Review-Url: https://codereview.chromium.org/2052113002
parent 9f343717
......@@ -32,8 +32,10 @@ import threading
import subprocess2
ROOT = os.path.abspath(os.path.dirname(__file__))
from StringIO import StringIO
ROOT = os.path.abspath(os.path.dirname(__file__))
IS_WIN = sys.platform == 'win32'
GIT_EXE = ROOT+'\\git.bat' if IS_WIN else 'git'
TEST_MODE = False
......@@ -392,6 +394,36 @@ def diff(oldrev, newrev, *args):
def freeze():
took_action = False
key = 'depot-tools.freeze-size-limit'
MB = 2**20
limit_mb = get_config_int(key, 100)
untracked_bytes = 0
for f, s in status():
if is_unmerged(s):
die("Cannot freeze unmerged changes!")
if limit_mb > 0:
if s.lstat == '?':
untracked_bytes += os.stat(f).st_size
if untracked_bytes > limit_mb * MB:
die("""\
You appear to have too much untracked+unignored data in your git
checkout: %.1f / %d MB.
Run `git status` to see what it is.
In addition to making many git commands slower, this will prevent
depot_tools from freezing your in-progress changes.
You should add untracked data that you want to ignore to your repo's
.git/info/excludes
file. See `git help ignore` for the format of this file.
If this data is indended as part of your commit, you may adjust the
freeze limit by running:
git config %s <new_limit>
Where <new_limit> is an integer threshold in megabytes.""",
untracked_bytes / (MB * 1.0), limit_mb, key)
try:
run('commit', '--no-verify', '-m', FREEZE + '.indexed')
......@@ -502,6 +534,13 @@ def is_dormant(branch):
return branch_config(branch, 'dormant', 'false') != 'false'
def is_unmerged(stat_value):
return (
'U' in (stat_value.lstat, stat_value.rstat) or
((stat_value.lstat == stat_value.rstat) and stat_value.lstat in 'AD')
)
def manual_merge_base(branch, base, parent):
set_branch_config(branch, 'base', base)
set_branch_config(branch, 'base-upstream', parent)
......@@ -711,6 +750,45 @@ def is_dirty_git_tree(cmd):
return False
def status():
"""Returns a parsed version of git-status.
Returns a generator of (current_name, (lstat, rstat, src)) pairs where:
* current_name is the name of the file
* lstat is the left status code letter from git-status
* rstat is the left status code letter from git-status
* src is the current name of the file, or the original name of the file
if lstat == 'R'
"""
stat_entry = collections.namedtuple('stat_entry', 'lstat rstat src')
def tokenizer(stream):
acc = StringIO()
c = None
while c != '':
c = stream.read(1)
if c in (None, '', '\0'):
if acc.len:
yield acc.getvalue()
acc = StringIO()
else:
acc.write(c)
def parser(tokens):
while True:
# Raises StopIteration if it runs out of tokens.
status_dest = next(tokens)
stat, dest = status_dest[:2], status_dest[3:]
lstat, rstat = stat
if lstat == 'R':
src = next(tokens)
else:
src = dest
yield (dest, stat_entry(lstat, rstat, src))
return parser(tokenizer(run_stream('status', '-z', bufsize=-1)))
def squash_current_branch(header=None, merge_base=None):
header = header or 'git squash commit.'
merge_base = merge_base or get_or_create_merge_base(current_branch())
......
......@@ -773,6 +773,8 @@ exactly the same working state later (by running <code>git thaw</code>).</p></di
with <em>git add</em>, <em>git mv</em>, <em>git rm</em>, etc.). A commit with the message
<code>FREEZE.unindexed</code> will contain all changes which were not in your index at the
time you ran git freeze (freshly modified files, new files, etc.).</p></div>
<div class="paragraph"><p>By default <code>git freeze</code> will only freeze up to 100MB of untracked files. See
<em>CONFIGURATION VARIABLES</em> for more details.</p></div>
</div>
</div>
<div class="sect1">
......@@ -788,7 +790,7 @@ time you ran git freeze (freshly modified files, new files, etc.).</p></div>
<span style="font-weight: bold; color: #ffffff">$ git freeze</span>
<span style="font-weight: bold; color: #ffffff">$ git status --short</span>
<span style="font-weight: bold; color: #ffffff">$ git log -n 2 --stat</span>
<span style="color: #e7e71c">commit 648c29b68da0142bcad41872339f9732c6ec4470</span>
<span style="color: #e7e71c">commit 07f208f5916bd8ee3affbf8d182c918a8dc1a699</span>
Author: local &lt;local@chromium.org&gt;
Date: Thu Apr 10 08:54:56 2014 +0000
......@@ -800,7 +802,7 @@ Date: Thu Apr 10 08:54:56 2014 +0000
unstaged_deleted_file | 1 <span style="color: #e42e16">-</span>
4 files changed, 2 insertions(+), 1 deletion(-)
<span style="color: #e7e71c">commit a4e49c2896814f52feec3fc06ef0454962ee6b8c</span>
<span style="color: #e7e71c">commit 9f803ff1f05dd0c77d31b8958992520e37ed6278</span>
Author: local &lt;local@chromium.org&gt;
Date: Thu Apr 10 08:54:56 2014 +0000
......@@ -822,6 +824,17 @@ Date: Thu Apr 10 08:54:56 2014 +0000
</div>
</div>
<div class="sect1">
<h2 id="_configuration_variables">CONFIGURATION VARIABLES</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_depot_tools_freeze_size_limit">depot-tools.freeze-size-limit</h3>
<div class="paragraph"><p>This sets the size limit as an integer number of megabytes of untracked files
that git-freeze will be willing to put in suspended animation. A 0 or negative
limit disables the size-limit check entirely. <strong>100</strong> by default.</p></div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_see_also">SEE ALSO</h2>
<div class="sectionbody">
<div class="paragraph"><p><a href="git-thaw.html">git-thaw(1)</a></p></div>
......@@ -839,7 +852,7 @@ from <a href="https://chromium.googlesource.com/chromium/tools/depot_tools.git">
<div id="footnotes"><hr /></div>
<div id="footer">
<div id="footer-text">
Last updated 2014-04-10 14:23:11 PDT
Last updated 2014-06-04 16:12:59 PDT
</div>
</div>
</body>
......
......@@ -2,12 +2,12 @@
.\" Title: git-freeze
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
.\" Date: 04/10/2014
.\" Date: 06/04/2014
.\" Manual: Chromium depot_tools Manual
.\" Source: depot_tools 68b1017
.\" Source: depot_tools d39bbb5
.\" Language: English
.\"
.TH "GIT\-FREEZE" "1" "04/10/2014" "depot_tools 68b1017" "Chromium depot_tools Manual"
.TH "GIT\-FREEZE" "1" "06/04/2014" "depot_tools d39bbb5" "Chromium depot_tools Manual"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
......@@ -40,6 +40,8 @@ git-freeze \- Freeze all changes on a branch (indexed and unindexed)\&.
git freeze works a lot like git stash, in that it stores the current changes in your working copy and index \fIsomewhere\fR\&. Unlike git stash, git freeze stores those changes on your current branch\&. This effectively allows you to \fIpause\fR development of a branch, work on something else, and then come back to exactly the same working state later (by running git thaw)\&.
.sp
git freeze will make up to 2 commits on your branch\&. A commit with the message FREEZE\&.indexed will contain all changes which you\(cqve added to your index (like with \fIgit add\fR, \fIgit mv\fR, \fIgit rm\fR, etc\&.)\&. A commit with the message FREEZE\&.unindexed will contain all changes which were not in your index at the time you ran git freeze (freshly modified files, new files, etc\&.)\&.
.sp
By default git freeze will only freeze up to 100MB of untracked files\&. See \fICONFIGURATION VARIABLES\fR for more details\&.
.SH "EXAMPLE"
.sp
......@@ -58,7 +60,7 @@ D deleted_file
\fB$ git freeze\fR
\fB$ git status \-\-short\fR
\fB$ git log \-n 2 \-\-stat\fR
commit 85b90eda860a6f62d95efdb29a09ebe2bace018d
commit 13e157e1f666206fca297c48bec82f3f16ae3c2f
Author: local <local@chromium\&.org>
Date: Thu Apr 10 08:54:56 2014 +0000
......@@ -70,7 +72,7 @@ Date: Thu Apr 10 08:54:56 2014 +0000
unstaged_deleted_file | 1 \-
4 files changed, 2 insertions(+), 1 deletion(\-)
commit 4d4db1b3bfbb42b0c9c87326bd522cc9d6946911
commit 30791edff23b9a8fecc5f863da134e820a4c9f1a
Author: local <local@chromium\&.org>
Date: Thu Apr 10 08:54:56 2014 +0000
......@@ -93,6 +95,10 @@ D deleted_file
.RE
.\}
.sp
.SH "CONFIGURATION VARIABLES"
.SS "depot\-tools\&.freeze\-size\-limit"
.sp
This sets the size limit as an integer number of megabytes of untracked files that git\-freeze will be willing to put in suspended animation\&. A 0 or negative limit disables the size\-limit check entirely\&. \fB100\fR by default\&.
.SH "SEE ALSO"
.sp
\fBgit-thaw\fR(1)
......
......@@ -26,10 +26,25 @@ with 'git add', 'git mv', 'git rm', etc.). A commit with the message
`FREEZE.unindexed` will contain all changes which were not in your index at the
time you ran git freeze (freshly modified files, new files, etc.).
By default `git freeze` will only freeze up to 100MB of untracked files. See
'CONFIGURATION VARIABLES' for more details.
EXAMPLE
-------
demo:1[]
CONFIGURATION VARIABLES
-----------------------
depot-tools.freeze-size-limit
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This sets the size limit as an integer number of megabytes of untracked files
that git-freeze will be willing to put in suspended animation. A 0 or negative
limit disables the size-limit check entirely. *100* by default.
SEE ALSO
--------
linkgit:git-thaw[1]
......
......@@ -731,6 +731,23 @@ class GitMutableStructuredTest(git_test_utils.GitRepoReadWriteTestBase,
CAT DOG
""")
def testStatus(self):
def inner():
dictified_status = lambda: {
k: dict(v._asdict()) # pylint: disable=W0212
for k, v in self.repo.run(self.gc.status)
}
self.repo.git('mv', 'file', 'cat')
with open('COOL', 'w') as f:
f.write('Super cool file!')
self.assertDictEqual(
dictified_status(),
{'cat': {'lstat': 'R', 'rstat': ' ', 'src': 'file'},
'COOL': {'lstat': '?', 'rstat': '?', 'src': 'COOL'}}
)
self.repo.run(inner)
class GitFreezeThaw(git_test_utils.GitRepoReadWriteTestBase):
@classmethod
......@@ -809,6 +826,52 @@ class GitFreezeThaw(git_test_utils.GitRepoReadWriteTestBase):
self.repo.run(inner)
def testTooBig(self):
def inner():
self.repo.git('config', 'depot-tools.freeze-size-limit', '1')
with open('bigfile', 'w') as f:
chunk = 'NERDFACE' * 1024
for _ in xrange(128 * 2 + 1): # Just over 2 mb
f.write(chunk)
_, err = self.repo.capture_stdio(self.gc.freeze)
self.assertIn('too much untracked+unignored', err)
self.repo.run(inner)
def testTooBigMultipleFiles(self):
def inner():
self.repo.git('config', 'depot-tools.freeze-size-limit', '1')
for i in xrange(3):
with open('file%d' % i, 'w') as f:
chunk = 'NERDFACE' * 1024
for _ in xrange(50): # About 400k
f.write(chunk)
_, err = self.repo.capture_stdio(self.gc.freeze)
self.assertIn('too much untracked+unignored', err)
self.repo.run(inner)
def testMerge(self):
def inner():
self.repo.git('checkout', '-b', 'bad_merge_branch')
with open('bad_merge', 'w') as f:
f.write('bad_merge_left')
self.repo.git('add', 'bad_merge')
self.repo.git('commit', '-m', 'bad_merge')
self.repo.git('checkout', 'branch_D')
with open('bad_merge', 'w') as f:
f.write('bad_merge_right')
self.repo.git('add', 'bad_merge')
self.repo.git('commit', '-m', 'bad_merge_d')
self.repo.git('merge', 'bad_merge_branch')
_, err = self.repo.capture_stdio(self.gc.freeze)
self.assertIn('Cannot freeze unmerged changes', err)
self.repo.run(inner)
class GitMakeWorkdir(git_test_utils.GitRepoReadOnlyTestBase, GitCommonTestBase):
def setUp(self):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment