better error messages, empty filesystem, WIP on log
This commit is contained in:
parent
39fb386e5a
commit
bacc1c866e
|
@ -51,6 +51,7 @@ article#git-tutorial table { width: 77rem; margin-left: calc( ( ( 63rem - 77rem
|
|||
#git-tutorial tr:last-child td { border:thin solid #aaa; }
|
||||
#git-tutorial tr:hover td { opacity: 1; border:thin solid black; }
|
||||
#git-tutorial tr:last-child.different td, #git-tutorial .different td { border: thin solid black; opacity: 1; background: #f0f6f8; }
|
||||
#git-tutorial table .empty-filesystem { text-align: center !important; color: #444 !important; opacity: 1 !important; border: thin solid black !important; }
|
||||
#git-tutorial .specialchar { color: red; word-wrap: normal; }
|
||||
#git-tutorial .hex-prefix { color: lightgrey; }
|
||||
#git-tutorial .hex { color: brown; }
|
||||
|
@ -90,3 +91,7 @@ article#git-tutorial .onlytoc { display: none; }
|
|||
/* Highlight elements when a click on e.g. a hash scrolls to the destination */
|
||||
#git-tutorial .scroll-destination-hilite, #git-tutorial .scroll-destination-hilite td { transition: background 0.5s linear 0.5s, opacity 0.4s linear 0.5s; background: #ffd3d3 !important; opacity: 1 !important; }
|
||||
#git-tutorial .scroll-destination-lolite, #git-tutorial .scroll-destination-lolite td { transition: background linear 0.5s, opacity linear 0.5s; }
|
||||
#git-tutorial .trivia { opacity: 80%; border: thin solid slategrey; margin: 1em; padding: 0.3em; }
|
||||
#git-tutorial .trivia:before { content: "Trivia"; border-bottom: thin solid slategrey; display: block; text-align: center; }
|
||||
#git-tutorial .trivia table { min-width: 90%; max-width: 90%; width: 90%; margin-left: auto; margin-right: auto; }
|
||||
/*#git-tutorial .trivia table td.cell-contents, #git-tutorial .trivia table th.cell-contents { width: 30%; }*/
|
|
@ -472,6 +472,16 @@ function ___filesystem_to_table(fs, previous_filesystem) {
|
|||
for (var i = 0; i < entries.length; i++) {
|
||||
tbody.append(___format_entry(previous_filesystem, entries[i]));
|
||||
}
|
||||
if (entries.length == 0) {
|
||||
var tr_empty = document.createElement('tr');
|
||||
tbody.append(tr_empty);
|
||||
|
||||
var td_empty = document.createElement('td');
|
||||
tr_empty.append(td_empty);
|
||||
td_empty.setAttribute('colspan', '2');
|
||||
td_empty.classList.add('empty-filesystem');
|
||||
td_empty.innerText = "The filesystem is empty."
|
||||
}
|
||||
return table;
|
||||
}
|
||||
function ___filesystem_to_string(fs, just_table, previous_filesystem) {
|
||||
|
@ -514,21 +524,76 @@ function ___copyprintf_click(id) {
|
|||
elem.disabled = true;
|
||||
}
|
||||
}
|
||||
var global_filesystem=false;
|
||||
var ___script_log_header = ''
|
||||
+ 'var ___log = [];\n'
|
||||
+ 'var console = (function(real_console) {\n'
|
||||
+ ' return {\n'
|
||||
+ ' log: function() {\n'
|
||||
+ ' ___log[___log.length] = arguments;\n'
|
||||
+ ' real_console.log.apply(console, arguments);\n'
|
||||
+ ' },\n'
|
||||
+ ' assert: real_console.assert,\n'
|
||||
+ ' };\n'
|
||||
+ '})(window.console);\n'
|
||||
+ '\n';
|
||||
|
||||
function ___eval_result_to_string(filesystem, previous_filesystem, log) {
|
||||
return '<pre>' + log.map(function(l) { return l.map(function (x) { return x.toString(); }).join(', '); }).join('\n') + '</pre>'
|
||||
+ ___filesystem_to_string(filesystem, false, previous_filesystem);
|
||||
}
|
||||
function ___git_eval(current) {
|
||||
document.getElementById('hide-eval-' + current).style.display = '';
|
||||
var script = '';
|
||||
var script = ___script_log_header;
|
||||
for (i = 0; i <= current - 1; i++) {
|
||||
script += ___textarea_value(___global_editors[i]);
|
||||
}
|
||||
script += "\n var ___previous_filesystem = {}; for (k in filesystem) { ___previous_filesystem[k] = filesystem[k]; }\n";
|
||||
script += '\n'
|
||||
+ 'var ___previous_filesystem = {};\n'
|
||||
+ 'for (k in filesystem) { ___previous_filesystem[k] = filesystem[k]; }\n'
|
||||
+ '___log = [];\n';
|
||||
script += ___textarea_value(___global_editors[current]);
|
||||
script += "\n document.getElementById('out' + current).innerHTML = ___filesystem_to_string(filesystem, false, ___previous_filesystem); filesystem;";
|
||||
script += '\n'
|
||||
+ '"End of the script";\n'
|
||||
+ '\n'
|
||||
+ '\n'
|
||||
+ 'document.getElementById("out" + current).innerHTML = ___eval_result_to_string(filesystem, ___previous_filesystem, ___log);\n'
|
||||
+ 'filesystem;\n';
|
||||
try {
|
||||
global_filesystem = eval(script);
|
||||
document.getElementById('debug').innerText = script;
|
||||
eval(script);
|
||||
} catch (e) {
|
||||
// Stack traces usually include :line:column
|
||||
var rx = /:([0-9][0-9]*):[0-9][0-9]*/g;
|
||||
var linecol = rx.exec(''+e.stack);
|
||||
var line = null;
|
||||
if (linecol && linecol.length > 0) {
|
||||
line=parseInt(linecol[1]);
|
||||
} else {
|
||||
// Some older versions of Firefox and probably some other browsers use just :line
|
||||
var rx = /:([0-9][0-9]*)*/g;
|
||||
var justline = rx.exec(''+e.stack);
|
||||
if (justline && justline.length > 0) {
|
||||
line=parseInt(justline[1], 10);
|
||||
}
|
||||
}
|
||||
if (typeof(line) == 'number') {
|
||||
var lines = script.split('\n');
|
||||
if (line < lines.length) {
|
||||
var from = Math.max(0, line-2);
|
||||
var to = Math.min(lines.length - 1, line+2+1);
|
||||
var showline = ''
|
||||
+ 'Possible location of the error: near line ' + line + '\n'
|
||||
+ '\n'
|
||||
+ lines.slice(from, to).map(function(l, i) { return '' + (from + i) + ': ' + l; }).join('\n')
|
||||
+ '\n'
|
||||
+ '\n';
|
||||
}
|
||||
} else {
|
||||
var showline = 'Sorry, this tutorial could not pinpoint precisely\nthe location of the error.\n'
|
||||
+ 'The stacktrace below may contain more information.\n'
|
||||
}
|
||||
var error = ___specialchars("" + e + "\n\n" + e.stack);
|
||||
document.getElementById('out' + current).innerHTML = '<pre class="error">' + error + '</pre>';
|
||||
document.getElementById('out' + current).innerHTML = '<pre class="error">' + showline + error + '</pre>';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
73
index.html
73
index.html
|
@ -516,7 +516,16 @@ directly to a commit hash like
|
|||
or can point to another symbolic reference, in which case the <code>.git/HEAD</code> file
|
||||
will contain e.g. <code>refs/heads/main</code>.</p>
|
||||
|
||||
<p>Branches are simple files stored in <code>.git/refs/heads/name-of-the-branch</code></p>
|
||||
<p>Branches are simple files stored in <code>.git/refs/heads/name-of-the-branch</code>
|
||||
and usually contain a hash like
|
||||
<span id="example-reference-branch-hash">0123456789abcdef0123456789abcdef01234567</span>.</p>
|
||||
|
||||
<p>Tags are identical to branches in terms of representation. It seems that the only difference
|
||||
between tags and branches is the behaviour of <code>git checkout</code> and similar commands.
|
||||
These commands, as explained in <a href="git-checkout">the section about <code>git checkout</code></a> below,
|
||||
normally write <code>ref: refs/heads/name-of-branch</code> in <code>.git/HEAD</code> when
|
||||
checking out a branch, but write the hash of the target commit when checking out a tag or
|
||||
any other non-branch reference.</p>
|
||||
|
||||
<div id="example-reference"></div>
|
||||
<script class="example">
|
||||
|
@ -545,6 +554,7 @@ will contain e.g. <code>refs/heads/main</code>.</p>
|
|||
var head = 'proj/.git/HEAD';
|
||||
|
||||
document.getElementById('example-reference-head-hash').innerText = initial_commit_hash;
|
||||
document.getElementById('example-reference-branch-hash').innerText = initial_commit_hash;
|
||||
|
||||
var previous_names = [ main, readme, src, proj, initial_commit ];
|
||||
var names = [ main, readme, src, proj, initial_commit, main_branch, v1_0_tag, head ];
|
||||
|
@ -566,21 +576,77 @@ function trim_newline(s) {
|
|||
|
||||
<section id="git-symbolic-ref">
|
||||
<h1><code>git symbolic-ref</code></h1>
|
||||
<p><code>git symbolic-ref</code> is a low-level command which reads
|
||||
(and in the official GIT implementation also writes and updates)
|
||||
symbolic references given a path relative to <code>.git/</code>.
|
||||
For example, <code>git symbolic-ref HEAD</code> will read the
|
||||
contents of the file <code>.git/HEAD</code>, and if that file starts
|
||||
with <code>ref: </code>, the rest of the line will be returned.</p>
|
||||
|
||||
<textarea>
|
||||
function git_symbolic_ref(ref) {
|
||||
var ref_file = join_paths(current_directory, '.git/' + ref);
|
||||
if (exists(ref_file) && read(ref_file).startsWith('ref: ')) {
|
||||
return trim_newline(read(ref_file)).substr('ref: '.length);
|
||||
var result = trim_newline(read(ref_file)).substr('ref: '.length);
|
||||
var recursive = git_symbolic_ref(result);
|
||||
return recursive || result;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
</textarea>
|
||||
<div class="trivia">
|
||||
<p>The official implementation of GIT follows references recursively
|
||||
and returns the <code>path/to/file</code> of the last file of the
|
||||
form <code>ref: path/to/file</code>. In the example below,
|
||||
<code>git symbolic-ref HEAD</code> would
|
||||
<ul>
|
||||
<li>read the file <code>proj/.git/HEAD</code> which contains <code>ref: refs/heads/main</code>,</li>
|
||||
<li>follow that indirection and read the file <code>proj/.git/refs/heads/main</code> which contains <code>ref: refs/heads/other</code></li>
|
||||
<li>follow that indirection and read the file <code>proj/.git/refs/heads/other</code> which contains a hash</li>
|
||||
<li>return the last file path that contained a <code>ref:</code>, i.e. return the string <code>refs/heads/other</code></li>
|
||||
</ul>
|
||||
<div id="example-recursive-ref"></div>
|
||||
<script class="example">
|
||||
___example('example-recursive-ref', function() {
|
||||
var h2f = function(hash) { return 'proj/.git/objects/'+hash.substr(0,2)+'/'+hash.substr(2); }
|
||||
var main = h2f(hash_object(true, 'blob', false, 'src/main.scm'));
|
||||
var readme = h2f(hash_object(true, 'blob', false, 'README'));
|
||||
var src = h2f(store_tree("src", ["main.scm"], []));
|
||||
var proj = h2f(paths_to_tree(["README", "src/main.scm"]));
|
||||
|
||||
var initial_commit_hash = store_commit(
|
||||
paths_to_tree(["README", "src/main.scm"]),
|
||||
[],
|
||||
{name:'Ada', email:'ada@...', date:new Date(1617120803000), timezoneMinutes: +60},
|
||||
{name:'Ada', email:'ada@...', date:new Date(1617120803000), timezoneMinutes: +60},
|
||||
'Initial commit');
|
||||
var initial_commit = h2f(initial_commit_hash);
|
||||
|
||||
write('proj/.git/refs/heads/main', 'ref: refs/heads/other\n');
|
||||
var main_branch = 'proj/.git/refs/heads/main';
|
||||
|
||||
git_branch('other', initial_commit_hash, true);
|
||||
var other_branch = 'proj/.git/refs/heads/other';
|
||||
|
||||
git_init_head();
|
||||
var head = 'proj/.git/HEAD';
|
||||
|
||||
document.getElementById('example-reference-head-hash').innerText = initial_commit_hash;
|
||||
document.getElementById('example-reference-branch-hash').innerText = initial_commit_hash;
|
||||
|
||||
var previous_names = [ initial_commit ];
|
||||
var names = [ initial_commit, main_branch, other_branch, head ];
|
||||
return { filesystem: filesystem, names: names, previous_names: previous_names }
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="git-rev-parse">
|
||||
<h1><code>git rev-parse</code></h1>
|
||||
<textarea>
|
||||
console.log('hello', 'world', 3);
|
||||
function git_rev_parse(ref) {
|
||||
var symbolic_ref_target = git_symbolic_ref(ref);
|
||||
if (symbolic_ref_target) {
|
||||
|
@ -1052,11 +1118,12 @@ commands.</p>
|
|||
</section>
|
||||
|
||||
<div id="toc"></div>
|
||||
<pre id="debug"></pre>
|
||||
</article>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
var script = '';
|
||||
var script = ___script_log_header;
|
||||
var ta = document.getElementsByTagName('textarea');
|
||||
for (var j = 0; j < ta.length; j++) {
|
||||
if (ta[j] == document.getElementById('playground-reset')) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user