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 ...@@ -32,8 +32,10 @@ import threading
import subprocess2 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' IS_WIN = sys.platform == 'win32'
GIT_EXE = ROOT+'\\git.bat' if IS_WIN else 'git' GIT_EXE = ROOT+'\\git.bat' if IS_WIN else 'git'
TEST_MODE = False TEST_MODE = False
...@@ -392,6 +394,36 @@ def diff(oldrev, newrev, *args): ...@@ -392,6 +394,36 @@ def diff(oldrev, newrev, *args):
def freeze(): def freeze():
took_action = False 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: try:
run('commit', '--no-verify', '-m', FREEZE + '.indexed') run('commit', '--no-verify', '-m', FREEZE + '.indexed')
...@@ -502,6 +534,13 @@ def is_dormant(branch): ...@@ -502,6 +534,13 @@ def is_dormant(branch):
return branch_config(branch, 'dormant', 'false') != 'false' 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): def manual_merge_base(branch, base, parent):
set_branch_config(branch, 'base', base) set_branch_config(branch, 'base', base)
set_branch_config(branch, 'base-upstream', parent) set_branch_config(branch, 'base-upstream', parent)
...@@ -711,6 +750,45 @@ def is_dirty_git_tree(cmd): ...@@ -711,6 +750,45 @@ def is_dirty_git_tree(cmd):
return False 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): def squash_current_branch(header=None, merge_base=None):
header = header or 'git squash commit.' header = header or 'git squash commit.'
merge_base = merge_base or get_or_create_merge_base(current_branch()) 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 ...@@ -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 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 <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> 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> </div>
<div class="sect1"> <div class="sect1">
...@@ -788,7 +790,7 @@ time you ran git freeze (freshly modified files, new files, etc.).</p></div> ...@@ -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 freeze</span>
<span style="font-weight: bold; color: #ffffff">$ git status --short</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="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; Author: local &lt;local@chromium.org&gt;
Date: Thu Apr 10 08:54:56 2014 +0000 Date: Thu Apr 10 08:54:56 2014 +0000
...@@ -800,7 +802,7 @@ 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> unstaged_deleted_file | 1 <span style="color: #e42e16">-</span>
4 files changed, 2 insertions(+), 1 deletion(-) 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; Author: local &lt;local@chromium.org&gt;
Date: Thu Apr 10 08:54:56 2014 +0000 Date: Thu Apr 10 08:54:56 2014 +0000
...@@ -822,6 +824,17 @@ 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> </div>
<div class="sect1"> <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> <h2 id="_see_also">SEE ALSO</h2>
<div class="sectionbody"> <div class="sectionbody">
<div class="paragraph"><p><a href="git-thaw.html">git-thaw(1)</a></p></div> <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"> ...@@ -839,7 +852,7 @@ from <a href="https://chromium.googlesource.com/chromium/tools/depot_tools.git">
<div id="footnotes"><hr /></div> <div id="footnotes"><hr /></div>
<div id="footer"> <div id="footer">
<div id="footer-text"> <div id="footer-text">
Last updated 2014-04-10 14:23:11 PDT Last updated 2014-06-04 16:12:59 PDT
</div> </div>
</div> </div>
</body> </body>
......
...@@ -2,12 +2,12 @@ ...@@ -2,12 +2,12 @@
.\" Title: git-freeze .\" Title: git-freeze
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/> .\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
.\" Date: 04/10/2014 .\" Date: 06/04/2014
.\" Manual: Chromium depot_tools Manual .\" Manual: Chromium depot_tools Manual
.\" Source: depot_tools 68b1017 .\" Source: depot_tools d39bbb5
.\" Language: English .\" 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 .\" * Define some portability stuff
.\" ----------------------------------------------------------------- .\" -----------------------------------------------------------------
...@@ -40,6 +40,8 @@ git-freeze \- Freeze all changes on a branch (indexed and unindexed)\&. ...@@ -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)\&. 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 .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\&.)\&. 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" .SH "EXAMPLE"
.sp .sp
...@@ -58,7 +60,7 @@ D deleted_file ...@@ -58,7 +60,7 @@ D deleted_file
\fB$ git freeze\fR \fB$ git freeze\fR
\fB$ git status \-\-short\fR \fB$ git status \-\-short\fR
\fB$ git log \-n 2 \-\-stat\fR \fB$ git log \-n 2 \-\-stat\fR
commit 85b90eda860a6f62d95efdb29a09ebe2bace018d commit 13e157e1f666206fca297c48bec82f3f16ae3c2f
Author: local <local@chromium\&.org> Author: local <local@chromium\&.org>
Date: Thu Apr 10 08:54:56 2014 +0000 Date: Thu Apr 10 08:54:56 2014 +0000
...@@ -70,7 +72,7 @@ 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 \- unstaged_deleted_file | 1 \-
4 files changed, 2 insertions(+), 1 deletion(\-) 4 files changed, 2 insertions(+), 1 deletion(\-)
commit 4d4db1b3bfbb42b0c9c87326bd522cc9d6946911 commit 30791edff23b9a8fecc5f863da134e820a4c9f1a
Author: local <local@chromium\&.org> Author: local <local@chromium\&.org>
Date: Thu Apr 10 08:54:56 2014 +0000 Date: Thu Apr 10 08:54:56 2014 +0000
...@@ -93,6 +95,10 @@ D deleted_file ...@@ -93,6 +95,10 @@ D deleted_file
.RE .RE
.\} .\}
.sp .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" .SH "SEE ALSO"
.sp .sp
\fBgit-thaw\fR(1) \fBgit-thaw\fR(1)
......
...@@ -26,10 +26,25 @@ with 'git add', 'git mv', 'git rm', etc.). A commit with the message ...@@ -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 `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.). 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 EXAMPLE
------- -------
demo:1[] 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 SEE ALSO
-------- --------
linkgit:git-thaw[1] linkgit:git-thaw[1]
......
...@@ -731,6 +731,23 @@ class GitMutableStructuredTest(git_test_utils.GitRepoReadWriteTestBase, ...@@ -731,6 +731,23 @@ class GitMutableStructuredTest(git_test_utils.GitRepoReadWriteTestBase,
CAT DOG 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): class GitFreezeThaw(git_test_utils.GitRepoReadWriteTestBase):
@classmethod @classmethod
...@@ -809,6 +826,52 @@ class GitFreezeThaw(git_test_utils.GitRepoReadWriteTestBase): ...@@ -809,6 +826,52 @@ class GitFreezeThaw(git_test_utils.GitRepoReadWriteTestBase):
self.repo.run(inner) 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): class GitMakeWorkdir(git_test_utils.GitRepoReadOnlyTestBase, GitCommonTestBase):
def setUp(self): 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