#!/usr/bin/env node
#!/usr/bin/env node
I wrote Gitlet to show how Git works under the covers. I wrote it to be readable. I commented the code heavily.
If you are not familiar with the basic Git commands, you can read Git in six hundred words (below).
For a six thousand word deep dive into the innards of Git, you can read Git from the inside out.
Imagine you have a directory called alpha
. It contains a file
called number.txt
that contains the text first
.
You run git init
to set up alpha
as a Git repository.
You run git add number.txt
to add number.txt
to the index. The
index is a list of all the files that Git is keeping track of. It
maps filenames to the content of the files. It now has the mapping
number.txt -> first
. Running the add command has also added a
blob object containing first
to the Git object database.
 This does three things. First, it
creates a tree object in the objects database. This object
represents the list of items in the top level of the alpha
directory. This object has a pointer to the first
blob object
that was created when you ran git add
. Second, it creates a
commit object that represents the version of the repository that
you have just committed. This includes a pointer to the tree
object. Third, it points the master branch at the new commit
object.
You run git clone . ../beta
. This creates a new directory called
beta
. It initializes it as a Git repository. It copies the
objects in the alpha objects database to the beta objects
database. It points the master branch on beta at the commit object
that the master branch points at on the alpha repository. It sets
the index on beta to mirror the content of the first commit. It
updates your files - number.txt
- to mirror the index.
You move to the beta repository. You change the content of
number.txt
to second
.  The commit object that is created has a pointer
to its parent, the first commit. The commit command points the
master branch at the second commit.
You move back to the alpha repository. You run git remote add beta
../beta
. This sets the beta repository as a remote repository.

Under the covers, this runs git fetch beta master
. This finds the
objects for the second commit and copies them from the beta
repository to the alpha repository. It points alpha’s record of
beta’s master at the second commit object. It updates FETCH_HEAD
to show that the master branch was fetched from the beta
repository.
Under the covers, the pull command runs git merge
FETCH_HEAD
. This reads FETCH_HEAD
, which shows that the master
branch on the beta repository was the most recently fetched
branch. It gets the commit object that alpha’s record of beta’s
master is pointing at. This is the second commit. The master branch
on alpha is pointing at the first commit, which is the ancestor of
the second commit. This means that, to complete the merge, the
merge command can just point the master branch at the second
commit. The merge command updates the index to mirror the contents
of the second commit. It updates the working copy to mirror the
index.
You run git branch red
. This creates a branch called red
that
points at the second commit object.
You run git checkout red
. Before the checkout, HEAD
pointed at
the master branch. It now points at the red branch. This makes the
red branch the current branch.
You set the content of number.txt
to third
, run git add
numbers.txt
and run git commit -m third
.
You run git push beta red
. This finds the objects for the third
commit and copies them from the alpha repository to the beta
repository. It points the red branch on the beta repository at the
third commit object, and that’s it.
var fs = require("fs");
var nodePath = require("path");
var gitlet = module.exports = {
init() initializes the current directory as a new repository.
init: function(opts) {
Abort if already a repository.
if (files.inRepo()) { return; }
opts = opts || {};
Create a JS object that mirrors the Git basic directory structure.
var gitletStructure = {
HEAD: "ref: refs/heads/master\n",
If --bare
was passed, write to the Git config indicating
that the repository is bare. If --bare
was not passed,
write to the Git config saying the repository is not bare.
config: config.objToStr({ core: { "": { bare: opts.bare === true }}}),
objects: {},
refs: {
heads: {},
}
};
Write the standard Git directory structure using the
gitletStructure
JS object. If the repository is not bare,
put the directories inside the .gitlet
directory. If the
repository is bare, put them in the top level of the
repository.
files.writeFilesFromTree(opts.bare ? gitletStructure : { ".gitlet": gitletStructure },
process.cwd());
},
add() adds files that match path
to the index.
add: function(path, _) {
files.assertInRepo();
config.assertNotBare();
Get the paths of all the files matching path
.
var addedFiles = files.lsRecursive(path);
Abort if no files matched path
.
if (addedFiles.length === 0) {
throw new Error(files.pathFromRepoRoot(path) + " did not match any files");
Otherwise, use the update_index()
Git command to actually add
the files.
} else {
addedFiles.forEach(function(p) { gitlet.update_index(p, { add: true }); });
}
},
rm() removes files that match path
from the index.
rm: function(path, opts) {
files.assertInRepo();
config.assertNotBare();
opts = opts || {};
Get the paths of all files in the index that match path
.
var filesToRm = index.matchingFiles(path);
Abort if -f
was passed. The removal of files with changes is
not supported.
if (opts.f) {
throw new Error("unsupported");
Abort if no files matched path
.
} else if (filesToRm.length === 0) {
throw new Error(files.pathFromRepoRoot(path) + " did not match any files");
Abort if path
is a directory and -r
was not passed.
} else if (fs.existsSync(path) && fs.statSync(path).isDirectory() && !opts.r) {
throw new Error("not removing " + path + " recursively without -r");
} else {
Get a list of all files that are to be removed and have also been changed on disk. If this list is not empty then abort.
var changesToRm = util.intersection(diff.addedOrModifiedFiles(), filesToRm);
if (changesToRm.length > 0) {
throw new Error("these files have changes:\n" + changesToRm.join("\n") + "\n");
Otherwise, remove the files that match path
. Delete them
from disk and remove from the index.
} else {
filesToRm.map(files.workingCopyPath).filter(fs.existsSync).forEach(fs.unlinkSync);
filesToRm.forEach(function(p) { gitlet.update_index(p, { remove: true }); });
}
}
},
commit() creates a commit object that represents the current
state of the index, writes the commit to the objects
directory
and points HEAD
at the commit.
commit: function(opts) {
files.assertInRepo();
config.assertNotBare();
Write a tree set of tree objects that represent the current state of the index.
var treeHash = gitlet.write_tree();
var headDesc = refs.isHeadDetached() ? "detached HEAD" : refs.headBranchName();
Compare the hash of the tree object at the top of the tree that
was just written with the hash of the tree object that the
HEAD
commit points at. If they are the same, abort because
there is nothing new to commit.
if (refs.hash("HEAD") !== undefined &&
treeHash === objects.treeHash(objects.read(refs.hash("HEAD")))) {
throw new Error("# On " + headDesc + "\nnothing to commit, working directory clean");
} else {
Abort if the repository is in the merge state and there are unresolved merge conflicts.
var conflictedPaths = index.conflictedPaths();
if (merge.isMergeInProgress() && conflictedPaths.length > 0) {
throw new Error(conflictedPaths.map(function(p) { return "U " + p; }).join("\n") +
"\ncannot commit because you have unmerged files\n");
Otherwise, do the commit.
} else {
If the repository is in the merge state, use a pre-written
merge commit message. If the repository is not in the
merge state, use the message passed with -m
.
var m = merge.isMergeInProgress() ? files.read(files.gitletPath("MERGE_MSG")) : opts.m;
Write the new commit to the objects
directory.
var commitHash = objects.writeCommit(treeHash, m, refs.commitParentHashes());
Point HEAD
at new commit.
gitlet.update_ref("HEAD", commitHash);
If MERGE_HEAD
exists, the repository was in the merge
state. Remove MERGE_HEAD
and MERGE_MSG
to exit the merge
state. Report that the merge is complete.
if (merge.isMergeInProgress()) {
fs.unlinkSync(files.gitletPath("MERGE_MSG"));
refs.rm("MERGE_HEAD");
return "Merge made by the three-way strategy";
Repository was not in the merge state, so just report that the commit is complete.
} else {
return "[" + headDesc + " " + commitHash + "] " + m;
}
}
}
},
branch() creates a new branch that points at the commit that
HEAD
points at.
branch: function(name, opts) {
files.assertInRepo();
opts = opts || {};
If no branch name
was passed, list the local branches.
if (name === undefined) {
return Object.keys(refs.localHeads()).map(function(branch) {
return (branch === refs.headBranchName() ? "* " : " ") + branch;
}).join("\n") + "\n";
HEAD
is not pointing at a commit, so there is no commit for
the new branch to point at. Abort. This is most likely to
happen if the repository has no commits.
} else if (refs.hash("HEAD") === undefined) {
throw new Error(refs.headBranchName() + " not a valid object name");
Abort because a branch called name
already exists.
} else if (refs.exists(refs.toLocalRef(name))) {
throw new Error("A branch named " + name + " already exists");
Otherwise, create a new branch by creating a new file called
name
that contains the hash of the commit that HEAD
points
at.
} else {
gitlet.update_ref(refs.toLocalRef(name), refs.hash("HEAD"));
}
},
checkout() changes the index, working copy and HEAD
to
reflect the content of ref
. ref
might be a branch name or a
commit hash.
checkout: function(ref, _) {
files.assertInRepo();
config.assertNotBare();
Get the hash of the commit to check out.
var toHash = refs.hash(ref);
Abort if ref
cannot be found.
if (!objects.exists(toHash)) {
throw new Error(ref + " did not match any file(s) known to Gitlet");
Abort if the hash to check out points to an object that is a not a commit.
} else if (objects.type(objects.read(toHash)) !== "commit") {
throw new Error("reference is not a tree: " + ref);
Abort if ref
is the name of the branch currently checked out.
Abort if head is detached, ref
is a commit hash and HEAD
is
pointing at that hash.
} else if (ref === refs.headBranchName() ||
ref === files.read(files.gitletPath("HEAD"))) {
return "Already on " + ref;
} else {
Get a list of files changed in the working copy. Get a list of the files that are different in the head commit and the commit to check out. If any files appear in both lists then abort.
var paths = diff.changedFilesCommitWouldOverwrite(toHash);
if (paths.length > 0) {
throw new Error("local changes would be lost\n" + paths.join("\n") + "\n");
Otherwise, perform the checkout.
} else {
process.chdir(files.workingCopyPath());
If the ref is in the objects
directory, it must be a hash
and so this checkout is detaching the head.
var isDetachingHead = objects.exists(ref);
Get the list of differences between the current commit and the commit to check out. Write them to the working copy.
workingCopy.write(diff.diff(refs.hash("HEAD"), toHash));
Write the commit being checked out to HEAD
. If the head
is being detached, the commit hash is written directly to
the HEAD
file. If the head is not being detached, the
branch being checked out is written to HEAD
.
refs.write("HEAD", isDetachingHead ? toHash : "ref: " + refs.toLocalRef(ref));
Set the index to the contents of the commit being checked out.
index.write(index.tocToIndex(objects.commitToc(toHash)));
Report the result of the checkout.
return isDetachingHead ?
"Note: checking out " + toHash + "\nYou are in detached HEAD state." :
"Switched to branch " + ref;
}
}
},
diff() shows the changes required to go from the ref1
commit to the ref2
commit.
diff: function(ref1, ref2, opts) {
files.assertInRepo();
config.assertNotBare();
Abort if ref1
was supplied, but it does not resolve to a
hash.
if (ref1 !== undefined && refs.hash(ref1) === undefined) {
throw new Error("ambiguous argument " + ref1 + ": unknown revision");
Abort if ref2
was supplied, but it does not resolve to a
hash.
} else if (ref2 !== undefined && refs.hash(ref2) === undefined) {
throw new Error("ambiguous argument " + ref2 + ": unknown revision");
Otherwise, perform diff.
} else {
Gitlet only shows the name of each changed file and whether it was added, modified or deleted. For simplicity, the changed content is not shown.
The diff happens between two versions of the repository. The
first version is either the hash that ref1
resolves to, or
the index. The second version is either the hash that ref2
resolves to, or the working copy.
var nameToStatus = diff.nameStatus(diff.diff(refs.hash(ref1), refs.hash(ref2)));
Show the path of each changed file.
return Object.keys(nameToStatus)
.map(function(path) { return nameToStatus[path] + " " + path; })
.join("\n") + "\n";
}
},
remote() records the locations of remote versions of this repository.
remote: function(command, name, path, _) {
files.assertInRepo();
Abort if command
is not “add”. Only “add” is supported.
if (command !== "add") {
throw new Error("unsupported");
Abort if repository already has a record for a remote called
name
.
} else if (name in config.read()["remote"]) {
throw new Error("remote " + name + " already exists");
Otherwise, add remote record.
} else {
Write to the config file a record of the name
and path
of
the remote.
config.write(util.setIn(config.read(), ["remote", name, "url", path]));
return "\n";
}
},
fetch() records the commit that branch
is at on remote
.
It does not change the local branch.
fetch: function(remote, branch, _) {
files.assertInRepo();
Abort if a remote
or branch
not passed.
if (remote === undefined || branch === undefined) {
throw new Error("unsupported");
Abort if remote
not recorded in config file.
} else if (!(remote in config.read().remote)) {
throw new Error(remote + " does not appear to be a git repository");
} else {
Get the location of the remote.
var remoteUrl = config.read().remote[remote].url;
Turn the unqualified branch name into a qualified remote ref
eg [branch] -> refs/remotes/[remote]/[branch]
var remoteRef = refs.toRemoteRef(remote, branch);
Go to the remote repository and get the hash of the commit
that branch
is on.
var newHash = util.onRemote(remoteUrl)(refs.hash, branch);
Abort if branch
did not exist on the remote.
if (newHash === undefined) {
throw new Error("couldn't find remote ref " + branch);
Otherwise, perform the fetch.
} else {
Note down the hash of the commit this repository currently thinks the remote branch is on.
var oldHash = refs.hash(remoteRef);
Get all the objects in the remote objects
directory and
write them. to the local objects
directory. (This is an
inefficient way of getting all the objects required to
recreate locally the commit the remote branch is on.)
var remoteObjects = util.onRemote(remoteUrl)(objects.allObjects);
remoteObjects.forEach(objects.write);
Set the contents of the file at
.gitlet/refs/remotes/[remote]/[branch]
to newHash
, the
hash of the commit that the remote branch is on.
gitlet.update_ref(remoteRef, newHash);
Record the hash of the commit that the remote branch is on
in FETCH_HEAD
. (The user can call gitlet merge
FETCH_HEAD
to merge the remote version of the branch into
their local branch. For more details, see
gitlet.merge().)
refs.write("FETCH_HEAD", newHash + " branch " + branch + " of " + remoteUrl);
Report the result of the fetch.
return ["From " + remoteUrl,
"Count " + remoteObjects.length,
branch + " -> " + remote + "/" + branch +
(merge.isAForceFetch(oldHash, newHash) ? " (forced)" : "")].join("\n") + "\n";
}
}
},
merge() finds the set of differences between the commit that
the currently checked out branch is on and the commit that ref
points to. It finds or creates a commit that applies these
differences to the checked out branch.
merge: function(ref, _) {
files.assertInRepo();
config.assertNotBare();
Get the receiverHash
, the hash of the commit that the
current branch is on.
var receiverHash = refs.hash("HEAD");
Get the giverHash
, the hash for the commit to merge into the
receiver commit.
var giverHash = refs.hash(ref);
Abort if head is detached. Merging into a detached head is not supported.
if (refs.isHeadDetached()) {
throw new Error("unsupported");
Abort if ref
did not resolve to a hash, or if that hash is
not for a commit object.
} else if (giverHash === undefined || objects.type(objects.read(giverHash)) !== "commit") {
throw new Error(ref + ": expected commit type");
Do not merge if the current branch - the receiver - already has the giver’s changes. This is the case if the receiver and giver are the same commit, or if the giver is an ancestor of the receiver.
} else if (objects.isUpToDate(receiverHash, giverHash)) {
return "Already up-to-date";
} else {
Get a list of files changed in the working copy. Get a list of the files that are different in the receiver and giver. If any files appear in both lists then abort.
var paths = diff.changedFilesCommitWouldOverwrite(giverHash);
if (paths.length > 0) {
throw new Error("local changes would be lost\n" + paths.join("\n") + "\n");
If the receiver is an ancestor of the giver, a fast forward is performed. This is possible because there is already a commit that incorporates all of the giver’s changes into the receiver.
} else if (merge.canFastForward(receiverHash, giverHash)) {
Fast forwarding means making the current branch reflect the
commit that giverHash
points at. The branch is pointed
at giverHash
. The index is set to match the contents of
the commit that giverHash
points at. The working copy is
set to match the contents of that commit.
merge.writeFastForwardMerge(receiverHash, giverHash);
return "Fast-forward";
If the receiver is not an ancestor of the giver, a merge commit must be created.
} else {
The repository is put into the merge state. The
MERGE_HEAD
file is written and its contents set to
giverHash
. The MERGE_MSG
file is written and its
contents set to a boilerplate merge commit message. A
merge diff is created that will turn the contents of
receiver into the contents of giver. This contains the
path of every file that is different and whether it was
added, removed or modified, or is in conflict. Added files
are added to the index and working copy. Removed files are
removed from the index and working copy. Modified files
are modified in the index and working copy. Files that are
in conflict are written to the working copy to include the
receiver and giver versions. Both the receiver and giver
versions are written to the index.
merge.writeNonFastForwardMerge(receiverHash, giverHash, ref);
If there are any conflicted files, a message is shown to say that the user must sort them out before the merge can be completed.
if (merge.hasConflicts(receiverHash, giverHash)) {
return "Automatic merge failed. Fix conflicts and commit the result.";
If there are no conflicted files, a commit is created from the merged changes and the merge is over.
} else {
return gitlet.commit();
}
}
}
},
pull() fetches the commit that branch
is on at remote
.
It merges that commit into the current branch.
pull: function(remote, branch, _) {
files.assertInRepo();
config.assertNotBare();
gitlet.fetch(remote, branch);
return gitlet.merge("FETCH_HEAD");
},
push() gets the commit that branch
is on in the local repo
and points branch
on remote
at the same commit.
push: function(remote, branch, opts) {
files.assertInRepo();
opts = opts || {};
Abort if a remote
or branch
not passed.
if (remote === undefined || branch === undefined) {
throw new Error("unsupported");
Abort if remote
not recorded in config file.
} else if (!(remote in config.read().remote)) {
throw new Error(remote + " does not appear to be a git repository");
} else {
var remotePath = config.read().remote[remote].url;
var remoteCall = util.onRemote(remotePath);
Abort if remote repository is not bare and branch
is
checked out.
if (remoteCall(refs.isCheckedOut, branch)) {
throw new Error("refusing to update checked out branch " + branch);
} else {
Get receiverHash
, the hash of the commit that branch
is
on at remote
.
var receiverHash = remoteCall(refs.hash, branch);
Get giverHash
, the hash of the commit that branch
is on
at the local repository.
var giverHash = refs.hash(branch);
Do nothing if the remote branch - the receiver - has
already incorporated the commit that giverHash
points
to. This is the case if the receiver commit and giver
commit are the same, or if the giver commit is an ancestor
of the receiver commit.
if (objects.isUpToDate(receiverHash, giverHash)) {
return "Already up-to-date";
Abort if branch
on remote
cannot be fast forwarded to
the commit that giverHash
points to. A fast forward can
only be done if the receiver commit is an ancestor of the
giver commit.
} else if (!opts.f && !merge.canFastForward(receiverHash, giverHash)) {
throw new Error("failed to push some refs to " + remotePath);
Otherwise, do the push.
} else {
Put all the objects in the local objects
directory into
the remote objects
directory.
objects.allObjects().forEach(function(o) { remoteCall(objects.write, o); });
Point branch
on remote
at giverHash
.
remoteCall(gitlet.update_ref, refs.toLocalRef(branch), giverHash);
Set the local repo’s record of what commit branch
is on
at remote
to giverHash
(since that is what it is now
is).
gitlet.update_ref(refs.toRemoteRef(remote, branch), giverHash);
Report the result of the push.
return ["To " + remotePath,
"Count " + objects.allObjects().length,
branch + " -> " + branch].join("\n") + "\n";
}
}
}
},
status() reports the state of the repo: the current branch, untracked files, conflicted files, files that are staged to be committed and files that are not staged to be committed.
status: function(_) {
files.assertInRepo();
config.assertNotBare();
return status.toString();
},
clone() copies the repository at remotePath
to
**targetPath
.
clone: function(remotePath, targetPath, opts) {
opts = opts || {};
Abort if a remotePath
or targetPath
not passed.
if (remotePath === undefined || targetPath === undefined) {
throw new Error("you must specify remote path and target path");
Abort if remotePath
does not exist, or is not a Gitlet
repository.
} else if (!fs.existsSync(remotePath) || !util.onRemote(remotePath)(files.inRepo)) {
throw new Error("repository " + remotePath + " does not exist");
Abort if targetPath
exists and is not empty.
} else if (fs.existsSync(targetPath) && fs.readdirSync(targetPath).length > 0) {
throw new Error(targetPath + " already exists and is not empty");
Otherwise, do the clone.
} else {
remotePath = nodePath.resolve(process.cwd(), remotePath);
If targetPath
doesn’t exist, create it.
if (!fs.existsSync(targetPath)) {
fs.mkdirSync(targetPath);
}
In the directory for the new remote repository…
util.onRemote(targetPath)(function() {
Initialize the directory as a Gitlet repository.
gitlet.init(opts);
Set up remotePath
as a remote called “origin”.
gitlet.remote("add", "origin", nodePath.relative(process.cwd(), remotePath));
Get the hash of the commit that master is pointing at on the remote repository.
var remoteHeadHash = util.onRemote(remotePath)(refs.hash, "master");
If the remote repo has any commits, that hash will exist.
The new repository records the commit that the passed
branch
is at on the remote. It then sets master on the
new repository to point at that commit.
if (remoteHeadHash !== undefined) {
gitlet.fetch("origin", "master");
merge.writeFastForwardMerge(undefined, remoteHeadHash);
}
});
Report the result of the clone.
return "Cloning into " + targetPath;
}
},
update_index() adds the contents of the file at path
to the
index, or removes the file from the index.
update_index: function(path, opts) {
files.assertInRepo();
config.assertNotBare();
opts = opts || {};
var pathFromRoot = files.pathFromRepoRoot(path);
var isOnDisk = fs.existsSync(path);
var isInIndex = index.hasFile(path, 0);
Abort if path
is a directory. update_index()
only handles
single files.
if (isOnDisk && fs.statSync(path).isDirectory()) {
throw new Error(pathFromRoot + " is a directory - add files inside\n");
} else if (opts.remove && !isOnDisk && isInIndex) {
Abort if file is being removed and is in conflict. Gitlet doesn’t support this.
if (index.isFileInConflict(path)) {
throw new Error("unsupported");
If files is being removed, is not on disk and is in the index, remove it from the index.
} else {
index.writeRm(path);
return "\n";
}
If file is being removed, is not on disk and not in the index, there is no work to do.
} else if (opts.remove && !isOnDisk && !isInIndex) {
return "\n";
Abort if the file is on disk and not in the index and the
--add
was not passed.
} else if (!opts.add && isOnDisk && !isInIndex) {
throw new Error("cannot add " + pathFromRoot + " to index - use --add option\n");
If file is on disk and either -add
was passed or the file is
in the index, add the file’s current content to the index.
} else if (isOnDisk && (opts.add || isInIndex)) {
index.writeNonConflict(path, files.read(files.workingCopyPath(path)));
return "\n";
Abort if the file is not on disk and --remove
not passed.
} else if (!opts.remove && !isOnDisk) {
throw new Error(pathFromRoot + " does not exist and --remove not passed\n");
}
},
write_tree() takes the content of the index and stores a tree
object that represents that content to the objects
directory.
write_tree: function(_) {
files.assertInRepo();
return objects.writeTree(files.nestFlatTree(index.toc()));
},
update_ref() gets the hash of the commit that refToUpdateTo
points at and sets refToUpdate
to point at the same hash.
update_ref: function(refToUpdate, refToUpdateTo, _) {
files.assertInRepo();
Get the hash that refToUpdateTo
points at.
var hash = refs.hash(refToUpdateTo);
Abort if refToUpdateTo
does not point at a hash.
if (!objects.exists(hash)) {
throw new Error(refToUpdateTo + " not a valid SHA1");
Abort if refToUpdate
does not match the syntax of a ref.
} else if (!refs.isRef(refToUpdate)) {
throw new Error("cannot lock the ref " + refToUpdate);
Abort if hash
points to an object in the objects
directory
that is not a commit.
} else if (objects.type(objects.read(hash)) !== "commit") {
var branch = refs.terminalRef(refToUpdate);
throw new Error(branch + " cannot refer to non-commit object " + hash + "\n");
Otherwise, set the contents of the file that the ref represents
to hash
.
} else {
refs.write(refs.terminalRef(refToUpdate), hash);
}
}
};
Refs are names for commit hashes. The ref is the name of a file.
Some refs represent local branches, like refs/heads/master
or
refs/heads/feature
. Some represent remote branches, like
refs/remotes/origin/master
. Some represent important states of
the repository, like HEAD
, MERGE_HEAD
and FETCH_HEAD
. Ref
files contain either a hash or another ref.
var refs = {
isRef() returns true if ref
matches valid qualified ref
syntax.
isRef: function(ref) {
return ref !== undefined &&
(ref.match("^refs/heads/[A-Za-z-]+$") ||
ref.match("^refs/remotes/[A-Za-z-]+/[A-Za-z-]+$") ||
["HEAD", "FETCH_HEAD", "MERGE_HEAD"].indexOf(ref) !== -1);
},
terminalRef() resolves ref
to the most specific ref
possible.
terminalRef: function(ref) {
If ref
is “HEAD” and head is pointing at a branch, return the
branch.
if (ref === "HEAD" && !refs.isHeadDetached()) {
return files.read(files.gitletPath("HEAD")).match("ref: (refs/heads/.+)")[1];
If ref is qualified, return it.
} else if (refs.isRef(ref)) {
return ref;
Otherwise, assume ref is an unqualified local ref (like
master
) and turn it into a qualified ref (like
refs/heads/master
)
} else {
return refs.toLocalRef(ref);
}
},
hash() returns the hash that refOrHash
points to.
hash: function(refOrHash) {
if (objects.exists(refOrHash)) {
return refOrHash;
} else {
var terminalRef = refs.terminalRef(refOrHash);
if (terminalRef === "FETCH_HEAD") {
return refs.fetchHeadBranchToMerge(refs.headBranchName());
} else if (refs.exists(terminalRef)) {
return files.read(files.gitletPath(terminalRef));
}
}
},
isHeadDetached() returns true if HEAD
contains a commit
hash, rather than the ref of a branch.
isHeadDetached: function() {
return files.read(files.gitletPath("HEAD")).match("refs") === null;
},
isCheckedOut() returns true if the repository is not bare and
HEAD
is pointing at the branch called branch
isCheckedOut: function(branch) {
return !config.isBare() && refs.headBranchName() === branch;
},
toLocalRef() converts the branch name name
into a qualified
local branch ref.
toLocalRef: function(name) {
return "refs/heads/" + name;
},
toRemoteRef() converts remote
and branch name name
into a
qualified remote branch ref.
toRemoteRef: function(remote, name) {
return "refs/remotes/" + remote + "/" + name;
},
write() sets the content of the file for the qualified ref
ref
to content
.
write: function(ref, content) {
if (refs.isRef(ref)) {
files.write(files.gitletPath(nodePath.normalize(ref)), content);
}
},
rm() removes the file for the qualified ref ref
.
rm: function(ref) {
if (refs.isRef(ref)) {
fs.unlinkSync(files.gitletPath(ref));
}
},
fetchHeadBranchToMerge() reads the FETCH_HEAD
file and gets
the hash that the remote branchName
is pointing at. For more
information about FETCH_HEAD
see gitlet.fetch().
fetchHeadBranchToMerge: function(branchName) {
return util.lines(files.read(files.gitletPath("FETCH_HEAD")))
.filter(function(l) { return l.match("^.+ branch " + branchName + " of"); })
.map(function(l) { return l.match("^([^ ]+) ")[1]; })[0];
},
localHeads() returns a JS object that maps local branch names to the hash of the commit they point to.
localHeads: function() {
return fs.readdirSync(nodePath.join(files.gitletPath(), "refs", "heads"))
.reduce(function(o, n) { return util.setIn(o, [n, refs.hash(n)]); }, {});
},
exists() returns true if the qualified ref ref
exists.
exists: function(ref) {
return refs.isRef(ref) && fs.existsSync(files.gitletPath(ref));
},
headBranchName() returns the name of the branch that HEAD
is pointing at.
headBranchName: function() {
if (!refs.isHeadDetached()) {
return files.read(files.gitletPath("HEAD")).match("refs/heads/(.+)")[1];
}
},
commitParentHashes() returns the array of commits that would be the parents of the next commit.
commitParentHashes: function() {
var headHash = refs.hash("HEAD");
If the repository is in the middle of a merge, return the hashes of the two commits being merged.
if (merge.isMergeInProgress()) {
return [headHash, refs.hash("MERGE_HEAD")];
If this repository has no commits, return an empty array.
} else if (headHash === undefined) {
return [];
Otherwise, return the hash of the commit that HEAD
is
currently pointing at.
} else {
return [headHash];
}
}
};
Objects are files in the .gitlet/objects/
directory.
numbers.txt
that contains first
is added to the
index, a blob called hash(first)
will be created containing
"first"
.
var objects = {
writeTree() stores a graph of tree objects that represent the content currently in the index.
writeTree: function(tree) {
var treeObject = Object.keys(tree).map(function(key) {
if (util.isString(tree[key])) {
return "blob " + tree[key] + " " + key;
} else {
return "tree " + objects.writeTree(tree[key]) + " " + key;
}
}).join("\n") + "\n";
return objects.write(treeObject);
},
fileTree() takes a tree hash and finds the corresponding tree
object. It reads the connected graph of tree objects into a
nested JS object, like:
{ file1: "hash(1)", src: { file2: "hash(2)" }
fileTree: function(treeHash, tree) {
if (tree === undefined) { return objects.fileTree(treeHash, {}); }
util.lines(objects.read(treeHash)).forEach(function(line) {
var lineTokens = line.split(/ /);
tree[lineTokens[2]] = lineTokens[0] === "tree" ?
objects.fileTree(lineTokens[1], {}) :
lineTokens[1];
});
return tree;
},
writeCommit() creates a commit object and writes it to the objects database.
writeCommit: function(treeHash, message, parentHashes) {
return objects.write("commit " + treeHash + "\n" +
parentHashes
.map(function(h) { return "parent " + h + "\n"; }).join("") +
"Date: " + new Date().toString() + "\n" +
"\n" +
" " + message + "\n");
},
write() writes str
to the objects database.
write: function(str) {
files.write(nodePath.join(files.gitletPath(), "objects", util.hash(str)), str);
return util.hash(str);
},
isUpToDate() returns true if the giver commit has already been incorporated into the receiver commit. That is, it returns true if the giver commit is an ancestor of the receiver, or they are the same commit.
isUpToDate: function(receiverHash, giverHash) {
return receiverHash !== undefined &&
(receiverHash === giverHash || objects.isAncestor(receiverHash, giverHash));
},
exists() returns true if there is an object in the database
called objectHash
exists: function(objectHash) {
return objectHash !== undefined &&
fs.existsSync(nodePath.join(files.gitletPath(), "objects", objectHash));
},
read() returns the content of the object called objectHash
.
read: function(objectHash) {
if (objectHash !== undefined) {
var objectPath = nodePath.join(files.gitletPath(), "objects", objectHash);
if (fs.existsSync(objectPath)) {
return files.read(objectPath);
}
}
},
allObjects() returns an array of the string content of all the objects in the database
allObjects: function() {
return fs.readdirSync(files.gitletPath("objects")).map(objects.read);
},
type() parses str
as an object and returns its type:
commit, tree or blob.
type: function(str) {
return { commit: "commit", tree: "tree", blob: "tree" }[str.split(" ")[0]] || "blob";
},
isAncestor() returns true if descendentHash
is a descendent
of ancestorHash
.
isAncestor: function(descendentHash, ancestorHash) {
return objects.ancestors(descendentHash).indexOf(ancestorHash) !== -1;
},
ancestors() returns an array of the hashes of all the
ancestor commits of commitHash
.
ancestors: function(commitHash) {
var parents = objects.parentHashes(objects.read(commitHash));
return util.flatten(parents.concat(parents.map(objects.ancestors)));
},
parentHashes() parses str
as a commit and returns the
hashes of its parents.
parentHashes: function(str) {
if (objects.type(str) === "commit") {
return str.split("\n")
.filter(function(line) { return line.match(/^parent/); })
.map(function(line) { return line.split(" ")[1]; });
}
},
parentHashes() parses str
as a commit and returns the tree
it points at.
treeHash: function(str) {
if (objects.type(str) === "commit") {
return str.split(/\s/)[1];
}
},
commitToc() takes the hash of a commit and reads the content
stored in the tree on the commit. It turns that tree into a
table of content that maps filenames to hashes of the files’
content, like: { "file1": hash(1), "a/file2": "hash(2)" }
commitToc: function(hash) {
return files.flattenNestedTree(objects.fileTree(objects.treeHash(objects.read(hash))));
}
};
The index maps files to hashes of their content. When a commit is created, a tree is built that mirrors the content of the index.
Index entry keys are actually a path,stage
combination. Stage is
always 0
, unless the entry is about a file that is in conflict.
See index.writeConflict()
for more details.
var index = {
hasFile() returns true if there is an entry for path
in the
index stage
.
hasFile: function(path, stage) {
return index.read()[index.key(path, stage)] !== undefined;
},
read() returns the index as a JS object.
read: function() {
var indexFilePath = files.gitletPath("index");
return util.lines(fs.existsSync(indexFilePath) ? files.read(indexFilePath) : "\n")
.reduce(function(idx, blobStr) {
var blobData = blobStr.split(/ /);
idx[index.key(blobData[0], blobData[1])] = blobData[2];
return idx;
}, {});
},
key() returns an index key made from path
and stage
.
key: function(path, stage) {
return path + "," + stage;
},
keyPieces() returns a JS object that contains the path and stage of ‘key`.
keyPieces: function(key) {
var pieces = key.split(/,/);
return { path: pieces[0], stage: parseInt(pieces[1]) };
},
toc() returns an object that maps file paths to hashes of
their content. This function is like read()
, except the JS
object it returns only uses the file path as a key.
toc: function() {
var idx = index.read();
return Object.keys(idx)
.reduce(function(obj, k) { return util.setIn(obj, [k.split(",")[0], idx[k]]); }, {});
},
isFileInConflict() returns true if the file for path
is in
conflict.
isFileInConflict: function(path) {
return index.hasFile(path, 2);
},
conflictedPaths() returns an array of all the paths of files that are in conflict.
conflictedPaths: function() {
var idx = index.read();
return Object.keys(idx)
.filter(function(k) { return index.keyPieces(k).stage === 2; })
.map(function(k) { return index.keyPieces(k).path; });
},
writeNonConflict() sets a non-conflicting index entry for the
file at path
to the hash of content
. (If the file was in
conflict, it is set to be no longer in conflict.)
writeNonConflict: function(path, content) {
Remove all keys for the file from the index.
index.writeRm(path);
Write a key for path
at stage 0
to indicate that the
file is not in conflict.
index._writeStageEntry(path, 0, content);
},
writeConflict() sets an index entry for the file
at path
that indicates the file is in conflict after a merge.
receiverContent
is the version of the file that is being merged
into. giverContent
is the version being merged in.
baseContent
is the version that the receiver and
giver both descended from.
writeConflict: function(path, receiverContent, giverContent, baseContent) {
if (baseContent !== undefined) {
Write a key for path
at stage 1
for baseContent
.
(There is no baseContent
if the same file was added for the
first time by both versions being merged.)
index._writeStageEntry(path, 1, baseContent);
}
Write a key for path
at stage 2
for receiverContent
.
index._writeStageEntry(path, 2, receiverContent);
Write a key for path
at stage 3
for giverContent
.
index._writeStageEntry(path, 3, giverContent);
},
writeRm() removes the index entry for the file at path
.
The file will be removed from the index even if it is in
conflict. (See index.writeConflict()
for more information on
conflicts.)
writeRm: function(path) {
var idx = index.read();
[0, 1, 2, 3].forEach(function(stage) { delete idx[index.key(path, stage)]; });
index.write(idx);
},
_writeStageEntry() adds the hashed content
to the index at
key path,stage
.
_writeStageEntry: function(path, stage, content) {
var idx = index.read();
idx[index.key(path, stage)] = objects.write(content);
index.write(idx);
},
write() takes a JS object that represents an index and writes
it to .gitlet/index
.
write: function(index) {
var indexStr = Object.keys(index)
.map(function(k) { return k.split(",")[0] + " " + k.split(",")[1] + " " + index[k] })
.join("\n") + "\n";
files.write(files.gitletPath("index"), indexStr);
},
workingCopyToc() returns an object that maps the file paths in the working copy to hashes of those files’ content.
workingCopyToc: function() {
return Object.keys(index.read())
.map(function(k) { return k.split(",")[0]; })
.filter(function(p) { return fs.existsSync(files.workingCopyPath(p)); })
.reduce(function(idx, p) {
idx[p] = util.hash(files.read(files.workingCopyPath(p)))
return idx;
}, {});
},
tocToIndex() takes an object that maps file paths to hashes
of the files’ content. It returns an object that is identical,
except the keys of the object are composed of the file paths and
stage 0
. eg: `{ “file1,0”: hash(1), “src/file2,0”: hash(2) }’
tocToIndex: function(toc) {
return Object.keys(toc)
.reduce(function(idx, p) { return util.setIn(idx, [index.key(p, 0), toc[p]]); }, {});
},
matchingFiles() returns all the paths in the index that match
pathSpec
. It matches relative to currentDir
.
matchingFiles: function(pathSpec) {
var searchPath = files.pathFromRepoRoot(pathSpec);
return Object.keys(index.toc())
.filter(function(p) { return p.match("^" + searchPath.replace(/\\/g, "\\\\")); });
}
};
Produces diffs between versions of the repository content. Diffs are represented as JS objects that map file paths to objects that indicate the change required to get from the first version of the file (the receiver) to the second (the giver). eg:
{ file1: { status: "A", receiver: undefined, base: undefined, giver: hash(1) }, file2: { status: "C", receiver: hash(b), base: hash(a), giver: hash(c) } }
var diff = {
FILE_STATUS: { ADD: "A", MODIFY: "M", DELETE: "D", SAME: "SAME", CONFLICT: "CONFLICT" },
diff() returns a diff object (see above for the format of a
diff object). If hash1
is passed, it is used as the first
version in the diff. If it is not passed, the index is used. If
hash2
is passed, it is used as the second version in the diff.
If it is not passed, the working copy is used.
diff: function(hash1, hash2) {
var a = hash1 === undefined ? index.toc() : objects.commitToc(hash1);
var b = hash2 === undefined ? index.workingCopyToc() : objects.commitToc(hash2);
return diff.tocDiff(a, b);
},
nameStatus() takes a diff and returns a JS object that maps from file paths to file statuses.
nameStatus: function(dif) {
return Object.keys(dif)
.filter(function(p) { return dif[p].status !== diff.FILE_STATUS.SAME; })
.reduce(function(ns, p) { return util.setIn(ns, [p, dif[p].status]); }, {});
},
tocDiff() takes three JS objects that map file paths to
hashes of file content. It returns a diff between receiver
and
giver
(see the module description for the format). base
is
the version that is the most recent commen ancestor of the
receiver
and giver
. If base
is not passed, receiver
is
used as the base. The base is only passed when getting the diff
for a merge. This is the only time the conflict status might be
used.
tocDiff: function(receiver, giver, base) {
fileStatus() takes three strings that represent different
versions of the content of a file. It returns the change that
needs to be made to get from the receiver
to the giver
.
function fileStatus(receiver, giver, base) {
var receiverPresent = receiver !== undefined;
var basePresent = base !== undefined;
var giverPresent = giver !== undefined;
if (receiverPresent && giverPresent && receiver !== giver) {
if (receiver !== base && giver !== base) {
return diff.FILE_STATUS.CONFLICT;
} else {
return diff.FILE_STATUS.MODIFY;
}
} else if (receiver === giver) {
return diff.FILE_STATUS.SAME;
} else if ((!receiverPresent && !basePresent && giverPresent) ||
(receiverPresent && !basePresent && !giverPresent)) {
return diff.FILE_STATUS.ADD;
} else if ((receiverPresent && basePresent && !giverPresent) ||
(!receiverPresent && basePresent && giverPresent)) {
return diff.FILE_STATUS.DELETE;
}
};
If base
was not passed, use receiver
as the base.
base = base || receiver;
Get an array of all the paths in all the versions.
var paths = Object.keys(receiver).concat(Object.keys(base)).concat(Object.keys(giver));
Create and return diff.
return util.unique(paths).reduce(function(idx, p) {
return util.setIn(idx, [p, {
status: fileStatus(receiver[p], giver[p], base[p]),
receiver: receiver[p],
base: base[p],
giver: giver[p]
}]);
}, {});
},
changedFilesCommitWouldOverwrite() gets a list of files changed in the working copy. It gets a list of the files that are different in the head commit and the commit for the passed hash. It returns a list of paths that appear in both lists.
changedFilesCommitWouldOverwrite: function(hash) {
var headHash = refs.hash("HEAD");
return util.intersection(Object.keys(diff.nameStatus(diff.diff(headHash))),
Object.keys(diff.nameStatus(diff.diff(headHash, hash))));
},
addedOrModifiedFiles() returns a list of files that have been added to or modified in the working copy since the last commit.
addedOrModifiedFiles: function() {
var headToc = refs.hash("HEAD") ? objects.commitToc(refs.hash("HEAD")) : {};
var wc = diff.nameStatus(diff.tocDiff(headToc, index.workingCopyToc()));
return Object.keys(wc).filter(function(p) { return wc[p] !== diff.FILE_STATUS.DELETE; });
}
};
var merge = {
commonAncestor() returns the hash of the commit that is the
most recent common ancestor of aHash
and bHash
.
commonAncestor: function(aHash, bHash) {
var sorted = [aHash, bHash].sort();
aHash = sorted[0];
bHash = sorted[1];
var aAncestors = [aHash].concat(objects.ancestors(aHash));
var bAncestors = [bHash].concat(objects.ancestors(bHash));
return util.intersection(aAncestors, bAncestors)[0];
},
isMergeInProgress() returns true if the repository is in the middle of a merge.
isMergeInProgress: function() {
return refs.hash("MERGE_HEAD");
},
canFastForward() A fast forward is possible if the changes
made to get to the giverHash
commit already incorporate the
changes made to get to the receiverHash
commit. So,
canFastForward()
returns true if the receiverHash
commit is
an ancestor of the giverHash
commit. It also returns true if
there is no receiverHash
commit because this indicates the
repository has no commits, yet.
canFastForward: function(receiverHash, giverHash) {
return receiverHash === undefined || objects.isAncestor(giverHash, receiverHash);
},
isAForceFetch() returns true if hash for local commit
(receiverHash
) is not ancestor of hash for fetched commit
(giverHash
).
isAForceFetch: function(receiverHash, giverHash) {
return receiverHash !== undefined && !objects.isAncestor(giverHash, receiverHash);
},
hasConflicts() returns true if merging the commit for
giverHash
into the commit for receiverHash
would produce
conflicts.
hasConflicts: function(receiverHash, giverHash) {
var mergeDiff = merge.mergeDiff(receiverHash, giverHash);
return Object.keys(mergeDiff)
.filter(function(p){return mergeDiff[p].status===diff.FILE_STATUS.CONFLICT }).length > 0
},
mergeDiff() returns a diff that represents the changes to get
from the receiverHash
commit to the giverHash
commit.
Because this is a merge diff, the function uses the common
ancestor of the receiverHash
commit and giverHash
commit to
avoid trivial conflicts.
mergeDiff: function(receiverHash, giverHash) {
return diff.tocDiff(objects.commitToc(receiverHash),
objects.commitToc(giverHash),
objects.commitToc(merge.commonAncestor(receiverHash, giverHash)));
},
writeMergeMsg() creates a message for the merge commit that
will potentially be created when the giverHash
commit is merged
into the receiverHash
commit. It writes this message to
.gitlet/MERGE_MSG
.
writeMergeMsg: function(receiverHash, giverHash, ref) {
var msg = "Merge " + ref + " into " + refs.headBranchName();
var mergeDiff = merge.mergeDiff(receiverHash, giverHash);
var conflicts = Object.keys(mergeDiff)
.filter(function(p) { return mergeDiff[p].status === diff.FILE_STATUS.CONFLICT });
if (conflicts.length > 0) {
msg += "\nConflicts:\n" + conflicts.join("\n");
}
files.write(files.gitletPath("MERGE_MSG"), msg);
},
writeIndex() merges the giverHash
commit into the
receiverHash
commit and writes the merged content to the index.
writeIndex: function(receiverHash, giverHash) {
var mergeDiff = merge.mergeDiff(receiverHash, giverHash);
index.write({});
Object.keys(mergeDiff).forEach(function(p) {
if (mergeDiff[p].status === diff.FILE_STATUS.CONFLICT) {
index.writeConflict(p,
objects.read(mergeDiff[p].receiver),
objects.read(mergeDiff[p].giver),
objects.read(mergeDiff[p].base));
} else if (mergeDiff[p].status === diff.FILE_STATUS.MODIFY) {
index.writeNonConflict(p, objects.read(mergeDiff[p].giver));
} else if (mergeDiff[p].status === diff.FILE_STATUS.ADD ||
mergeDiff[p].status === diff.FILE_STATUS.SAME) {
var content = objects.read(mergeDiff[p].receiver || mergeDiff[p].giver);
index.writeNonConflict(p, content);
}
});
},
writeFastForwardMerge() Fast forwarding means making the
current branch reflect the commit that giverHash
points at. No
new commit is created.
writeFastForwardMerge: function(receiverHash, giverHash) {
Point head at giverHash
.
refs.write(refs.toLocalRef(refs.headBranchName()), giverHash);
Make the index mirror the content of giverHash
.
index.write(index.tocToIndex(objects.commitToc(giverHash)));
If the repo is bare, it has no working copy, so there is no more work to do. If the repo is not bare…
if (!config.isBare()) {
…Get an object that maps from file paths in the
receiverHash
commit to hashes of the files’ content. If
recevierHash
is undefined, the repository has no commits,
yet, and the mapping object is empty.
var receiverToc = receiverHash === undefined ? {} : objects.commitToc(receiverHash);
…and write the content of the files to the working copy.
workingCopy.write(diff.tocDiff(receiverToc, objects.commitToc(giverHash)));
}
},
writeNonFastForwardMerge() A non fast forward merge creates a
merge commit to integrate the content of the receiverHash
commit with the content of the giverHash
commit. This
integration requires a merge commit because, unlike a fast
forward merge, no commit yet exists that embodies the combination
of these two commits. writeNonFastForwardMerge()
does not
actually create the merge commit. It just sets the wheels in
motion.
writeNonFastForwardMerge: function(receiverHash, giverHash, giverRef) {
Write giverHash
to .gitlet/MERGE_HEAD
. This file acts as a
record of giverHash
and as the signal that the repository is
in the merging state.
refs.write("MERGE_HEAD", giverHash);
Write a standard merge commit message that will be used when the merge commit is created.
merge.writeMergeMsg(receiverHash, giverHash, giverRef);
Merge the receiverHash
commit with the giverHash
commit and
write the content to the index.
merge.writeIndex(receiverHash, giverHash);
If the repo is bare, it has no working copy, so there is no more work to do. If the repo is not bare…
if (!config.isBare()) {
…merge the receiverHash
commit with the giverHash
commit and write the content to the working copy.
workingCopy.write(merge.mergeDiff(receiverHash, giverHash));
}
}
};
The working copy is the set of files that are inside the
repository, excluding the .gitlet
directory.
var workingCopy = {
write() takes a diff object (see the diff module for a description of the format) and applies the changes in it to the working copy.
write: function(dif) {
composeConflict()
takes the hashes of two versions of the
same file and returns a string that represents the two versions
as a conflicted file:
<<<<<
version1
======
version2
>>>>>
Note that Gitlet, unlike real Git, does not do a line by line
diff and mark only the conflicted parts of the file. If a file
is in conflict, the whole body of the file is marked as one big
conflict.
function composeConflict(receiverFileHash, giverFileHash) {
return "<<<<<<\n" + objects.read(receiverFileHash) +
"\n======\n" + objects.read(giverFileHash) +
"\n>>>>>>\n";
};
Go through all the files that have changed, updating the working copy for each.
Object.keys(dif).forEach(function(p) {
if (dif[p].status === diff.FILE_STATUS.ADD) {
files.write(files.workingCopyPath(p), objects.read(dif[p].receiver || dif[p].giver));
} else if (dif[p].status === diff.FILE_STATUS.CONFLICT) {
files.write(files.workingCopyPath(p), composeConflict(dif[p].receiver, dif[p].giver));
} else if (dif[p].status === diff.FILE_STATUS.MODIFY) {
files.write(files.workingCopyPath(p), objects.read(dif[p].giver));
} else if (dif[p].status === diff.FILE_STATUS.DELETE) {
fs.unlinkSync(files.workingCopyPath(p));
}
});
Remove any directories that have been left empty after the deletion of all the files in them.
fs.readdirSync(files.workingCopyPath())
.filter(function(n) { return n !== ".gitlet"; })
.forEach(files.rmEmptyDirs);
}
};
This code allows the config file at .gitlet/config
to be read and
written.
var config = {
isBare() returns true if the repository is bare.
isBare: function() {
return config.read().core[""].bare === "true";
},
assertNotBare() throws if the repository is bare.
assertNotBare: function() {
if (config.isBare()) {
throw new Error("this operation must be run in a work tree");
}
},
read() returns the contents of the config file as a nested JS object.
read: function() {
return config.strToObj(files.read(files.gitletPath("config")));
},
write() stringifies the nested JS object configObj
and
overwrites the config file with it.
write: function(configObj) {
files.write(files.gitletPath("config"), config.objToStr(configObj));
},
strToObj() parses the config string str
and returns its
contents as a nested JS object.
strToObj: function(str) {
return str.split("[")
.map(function(item) { return item.trim(); })
.filter(function(item) { return item !== ""; })
.reduce(function(c, item) {
var lines = item.split("\n");
var entry = [];
section eg “core”
entry.push(lines[0].match(/([^ \]]+)( |\])/)[1]);
eg “master”
var subsectionMatch = lines[0].match(/\"(.+)\"/);
var subsection = subsectionMatch === null ? "" : subsectionMatch[1];
entry.push(subsection);
options and their values
entry.push(lines.slice(1).reduce(function(s, l) {
s[l.split("=")[0].trim()] = l.split("=")[1].trim();
return s;
}, {}));
return util.setIn(c, entry);
}, { "remote": {} });
},
objToStr() configObj
is a JS object that holds the config
for the repository. objToStr()
stringifies the object and
returns the string.
objToStr: function(configObj) {
return Object.keys(configObj)
.reduce(function(arr, section) {
return arr.concat(
Object.keys(configObj[section])
.map(function(subsection) { return { section: section, subsection: subsection }})
);
}, [])
.map(function(entry) {
var subsection = entry.subsection === "" ? "" : " \"" + entry.subsection +"\"";
var settings = configObj[entry.section][entry.subsection];
return "[" + entry.section + subsection + "]\n" +
Object.keys(settings)
.map(function(k) { return " " + k + " = " + settings[k]; })
.join("\n") + "\n";
})
.join("");
}
};
A set of handy functions.
var util = {
isString() returns true if thing
is a string.
isString: function(thing) {
return typeof thing === "string";
},
hash() returns a hash of string
.
hash: function(string) {
var hashInt = 0;
for (var i = 0; i < string.length; i++) {
hashInt = hashInt * 31 + string.charCodeAt(i);
hashInt = hashInt | 0;
}
return Math.abs(hashInt).toString(16);
},
setIn() takes an array that contains 1 or more keys and has
one value at the end. It drills down into obj
using the keys
and sets the value as the value of the last key. eg
setIn({}, ["a", "b", "me"]); // => { a: { b: "me" } }
setIn: function(obj, arr) {
if (arr.length === 2) {
obj[arr[0]] = arr[1];
} else if (arr.length > 2) {
obj[arr[0]] = obj[arr[0]] || {};
util.setIn(obj[arr[0]], arr.slice(1));
}
return obj;
},
lines() takes a string, splits on newlines and returns an array of the lines that are not empty.
lines: function(str) {
return str.split("\n").filter(function(l) { return l !== ""; });
},
flatten() returns a flattened version of arr
.
flatten: function(arr) {
return arr.reduce(function(a, e) {
return a.concat(e instanceof Array ? util.flatten(e) : e);
}, []);
},
unique() returns the unique elements in arr
.
unique: function(arr) {
return arr.reduce(function(a, p) { return a.indexOf(p) === -1 ? a.concat(p) : a; }, []);
},
intersection() takes two arrays a
and b
. It returns an
array of the items that appear in both.
intersection: function(a, b) {
return a.filter(function(e) { return b.indexOf(e) !== -1; });
},
onRemote() allows execution of a command on a remote
repository. It returns an anonymous function that takes another
function fn
. When the anonymous function is run, it switches
to remotePath
, executes fn
, then switches back to the
original directory.
onRemote: function(remotePath) {
return function(fn) {
var originalDir = process.cwd();
process.chdir(remotePath);
var result = fn.apply(null, Array.prototype.slice.call(arguments, 1));
process.chdir(originalDir);
return result;
};
}
};
var files = {
inRepo() returns true if the current working directory is inside a repository.
inRepo: function() {
return files.gitletPath() !== undefined;
},
assertInRepo() throws if the current working directory is not inside a repository.
assertInRepo: function() {
if (!files.inRepo()) {
throw new Error("not a Gitlet repository");
}
},
pathFromRepoRoot() returns path
relative to the repo root
pathFromRepoRoot: function(path) {
return nodePath.relative(files.workingCopyPath(), nodePath.join(process.cwd(), path));
},
write() writes content
to file at path
, overwriting
anything that is already there.
write: function(path, content) {
var prefix = require("os").platform() == "win32" ? "." : "/";
files.writeFilesFromTree(util.setIn({}, path.split(nodePath.sep).concat(content)), prefix);
},
writeFilesFromTree() takes tree
of files as a nested JS obj
and writes all those files to disk taking prefix
as the root of
the tree. tree
format is: { a: { b: { c: "filecontent" }}}
writeFilesFromTree: function(tree, prefix) {
Object.keys(tree).forEach(function(name) {
var path = nodePath.join(prefix, name);
if (util.isString(tree[name])) {
fs.writeFileSync(path, tree[name]);
} else {
if (!fs.existsSync(path)) {
fs.mkdirSync(path, "777");
}
files.writeFilesFromTree(tree[name], path);
}
});
},
rmEmptyDirs() recursively removes all the empty directories
inside path
.
rmEmptyDirs: function(path) {
if (fs.statSync(path).isDirectory()) {
fs.readdirSync(path).forEach(function(c) { files.rmEmptyDirs(nodePath.join(path, c)); });
if (fs.readdirSync(path).length === 0) {
fs.rmdirSync(path);
}
}
},
read() returns the contents of the file at path
as a
string. It returns undefined
if the file doesn’t exist.
read: function(path) {
if (fs.existsSync(path)) {
return fs.readFileSync(path, "utf8");
}
},
gitletPath() returns a string made by concatenating path
to
the absolute path of the .gitlet
directory of the repository.
gitletPath: function(path) {
function gitletDir(dir) {
if (fs.existsSync(dir)) {
var potentialConfigFile = nodePath.join(dir, "config");
var potentialGitletPath = nodePath.join(dir, ".gitlet");
if (fs.existsSync(potentialConfigFile) &&
fs.statSync(potentialConfigFile).isFile() &&
files.read(potentialConfigFile).match(/\[core\]/)) {
return dir;
} else if (fs.existsSync(potentialGitletPath)) {
return potentialGitletPath;
} else if (dir !== "/") {
return gitletDir(nodePath.join(dir, ".."));
}
}
};
var gDir = gitletDir(process.cwd());
if (gDir !== undefined) {
return nodePath.join(gDir, path || "");
}
},
workingCopyPath() returns a string made by concatenating path
to
the absolute path of the root of the repository.
workingCopyPath: function(path) {
return nodePath.join(nodePath.join(files.gitletPath(), ".."), path || "");
},
lsRecursive() returns an array of all the files found in a
recursive search of path
.
lsRecursive: function(path) {
if (!fs.existsSync(path)) {
return [];
} else if (fs.statSync(path).isFile()) {
return [path];
} else if (fs.statSync(path).isDirectory()) {
return fs.readdirSync(path).reduce(function(fileList, dirChild) {
return fileList.concat(files.lsRecursive(nodePath.join(path, dirChild)));
}, []);
}
},
nestFlatTree() takes obj
, a mapping of file path strings to
content, and returns a nested JS obj where each key represents a
sub directory. This is the opposite of
flattenNestedTree()
eg nestFlatTree({ "a/b": "me" }); // => { a: { b: "me" }}
nestFlatTree: function(obj) {
return Object.keys(obj).reduce(function(tree, wholePath) {
return util.setIn(tree, wholePath.split(nodePath.sep).concat(obj[wholePath]));
}, {});
},
flattenNestedTree() takes tree
, a nested JS object where
each key represents a sub directory and returns a JS object
mapping file path strings to content. This is the opposite of
nestFlatTree()
eg flattenNestedTree({ a: { b: "me" }}); // => { "a/b": "me"}
flattenNestedTree: function(tree, obj, prefix) {
if (obj === undefined) { return files.flattenNestedTree(tree, {}, ""); }
Object.keys(tree).forEach(function(dir) {
var path = nodePath.join(prefix, dir);
if (util.isString(tree[dir])) {
obj[path] = tree[dir];
} else {
files.flattenNestedTree(tree[dir], obj, path);
}
});
return obj;
}
};
Outputs the repository status as a human-readable string.
var status = {
toString() returns the repository status as a human-readable string.
toString: function() {
untracked() returns an array of lines listing the files not being tracked by Gitlet.
function untracked() {
return fs.readdirSync(files.workingCopyPath())
.filter(function(p) { return index.toc()[p] === undefined && p !== ".gitlet"; });
};
toBeCommitted() returns an array of lines listing the files that have changes that will be included in the next commit.
function toBeCommitted() {
var headHash = refs.hash("HEAD");
var headToc = headHash === undefined ? {} : objects.commitToc(headHash);
var ns = diff.nameStatus(diff.tocDiff(headToc, index.toc()));
return Object.keys(ns).map(function(p) { return ns[p] + " " + p; });
};
notStagedForCommit() returns an array of lines listing the files that have changes that will not be included in the next commit.
function notStagedForCommit() {
var ns = diff.nameStatus(diff.diff());
return Object.keys(ns).map(function(p) { return ns[p] + " " + p; });
};
listing() keeps lines
(prefixed by heading
) only if it’s nonempty.
function listing(heading, lines) {
return lines.length > 0 ? [heading, lines] : [];
}
Gather all the sections, keeping only nonempty ones, and flatten them together into a string.
return util.flatten(["On branch " + refs.headBranchName(),
listing("Untracked files:", untracked()),
listing("Unmerged paths:", index.conflictedPaths()),
listing("Changes to be committed:", toBeCommitted()),
listing("Changes not staged for commit:", notStagedForCommit())])
.join("\n");
}
};
Gitlet can be used from the command line. For example, executing
node gitlet.js commit -m woo
would commit to the current repo
with the message “woo”.
parseOptions() takes the process.argv
object passed when
gitlet.js is run as a script. It returns an object that contains
the parsed parameters to be formed into a Gitlet command.
var parseOptions = function(argv) {
var name;
return argv.reduce(function(opts, arg) {
if (arg.match(/^-/)) {
name = arg.replace(/^-+/, "");
opts[name] = true;
} else if (name !== undefined) {
opts[name] = arg;
name = undefined;
} else {
opts._.push(arg);
}
return opts;
}, { _: [] });
};
runCli() takes the process.argv
object passed when gitlet.js
is run as a script. It parses the command line arguments, runs the
corresponding Gitlet command and returns the string returned by the
command.
var runCli = module.exports.runCli = function(argv) {
var opts = parseOptions(argv);
var commandName = opts._[2];
if (commandName === undefined) {
throw new Error("you must specify a Gitlet command to run");
} else {
var commandFnName = commandName.replace(/-/g, "_");
var fn = gitlet[commandFnName];
if (fn === undefined) {
throw new Error("'" + commandFnName + "' is not a Gitlet command");
} else {
var commandArgs = opts._.slice(3);
while (commandArgs.length < fn.length - 1) {
commandArgs.push(undefined);
}
return fn.apply(gitlet, commandArgs.concat(opts));
}
}
};
If gitlet.js
is run as a script, pass the process.argv
array of
script arguments to runCli()
so they can be used to run a Gitlet
command. Print the return value of the Gitlet command. If the
Gitlet command throws, print the error message.
if (require.main === module) {
try {
var result = runCli(process.argv);
if (result !== undefined) {
console.log(result);
}
} catch (e) {
console.error(e.toString());
}
}


stillingsok2000
GLOBAL
DETAILS
>
request_typeahead
STATISTICS
Executions
Total
OK
KO
Mean req/s
Response Time (ms)
Total
OK
KO
Min
50th percentile
75th percentile
95th percentile
99th percentile
Max
Mean
Std Deviation


Apache is the most widely used Web Server application in the world with more than 50% share in the commercial web server market. Virtual hosting is one such feature that allows a single Apache Web Server to serve a number of different websites. The word, Apache, has been taken from the name of the Native American tribe ‘Apache’, famous for its skills in warfare and strategy making.
Installing apache from the source require the –devel package to be installed on your server. You can find the latest available version of Apache, you can download it here:
http://httpd.apache.org/download.cgi .
Once you download the source file move it to the /usr/local/src folder.
[root@haktuts ~] cd /usr/local/src
>Go to /usr/local/src directory
[root@haktuts ~] gzip -d httpd-2.2.26.tar.gz
>Apache file is archived with .tar.gz so first use gzip command to extract the data from gzip.
[root@ haktuts ~] tar xvf httpd-2.2.26.tar
>Now use tar command to extract the data from .tar archived file.
[root@ haktuts ~] httpd-2.2.26
[root@ haktuts ~] ./configure –help
>see all configuration option
[root@ haktuts ~] ./configure –prefix=/usr/local/apache –enable-so
>The compilation of Apache within the /usr/local/apache directory with the DSO capability. The –enable-so option, can load required modules to apache at run time via the DSO mechanism rather than requiring a recompilation.
[root@ haktuts ~] make
[root@ haktuts ~] make install
>use to make the file and install
[root@ haktuts ~] iptables -I INPUT -p tcp --dport 80 -j ACCEPT
>command to open port 80 in firewell
There are two types of virtual hosts supported by Apache –
1.Name-based virtual host
2.Address-based or IP based virtual host
1.Name-based Virtual Host
Name based virtual hosting is used to host multiple websites on a single IP address.for this user have to edit the httpd.conf file & copy the colored content below and paste it.
NameVirtualHost *:80
<VirtualHost <IP_ADDRESS>:80>
ServerAdmin<EMAIL_ADDRESS>DocumentRoot /var/www/html/haktuts.com
ServerName www.haktuts.com
</VirtualHost>
<VirtualHost <IP_ADDRESS>:80>
ServerAdmin<EMAIL_ADDRESS>DocumentRoot /var/www/html/haktuts.in
ServerName www.haktuts.in
</VirtualHost>
Now check the configuration file and make sure all “syntax Ok” with below command
[root@haktuts ~]httpd –t
2.IP-based Virtual host
Listen <IP_ADDRESS>:80
<VirtualHost <IP_ADDRESS>:80>
ServerAdmin<EMAIL_ADDRESS>DocumentRoot /var/www/html/ haktuts.com
ServerName www. haktuts.com
</VirtualHost>
<VirtualHost <IP_ADDRESS>:80>
ServerAdmin tuts@ haktuts.in
DocumentRoot /var/www/html/ haktuts.in
ServerName www.haktuts.in
</VirtualHost>
In ip based hosting, multiple ip address is used as shown above for configuration
If user want to deploy the php website then mod_php enabled on your server
This file is find in /etc/httpd/conf.d/ directory.
Check the mod_php with following command
httpd -M | grep "php5_module"
| means pipe and show the details in table views i.e.in more format
grep is like finding the particular word in whole directory here we are looking for php5_module so command will be grep "php5_module"
© 2014 HaKTuts. All rights reserved.


mail
The command operates on the user's mailbox if no argument is given.
To send an email the message body is built from standard input.
More information: home page
- Send a typed email message. The command-line below continues after pressing Enter key. Input CC email-id (optional) press Enter key. Input message text (can be multiline). Press Ctrl-D key to complete the message text:
mail --subject="subject line"<EMAIL_ADDRESS>- Send an email that contains file content:
mail --subject="$HOSTNAME filename.txt"<EMAIL_ADDRESS>< path/to/filename.txt
- Send a `tar.gz` file as an attachment:
tar cvzf - path/to/directory1 path/to/directory2 | uuencode data.tar.gz | mail --subject="subject_line" to_user@example.com


This is the basic horizontal form with labels on left and form controls on right in one line. Add .form-horizontal class to the form tag to have horizontal form styling. To define form sections use form-section class with any heading tags.
.form-horizontal
form-section


--- layout: post title: 10 Ideas On Podcasts I Could Host category: 10ideas ---
About 6 months ago, I didn't listen to any podcasts. Now I often find my phone running out of memory because I have too many podcast episodes. Currently my favorite podcasts are Code Newbie, Ruby Rogues, and StartUp. I decided to think of 10 podcasts I could host.


..because he didn't do anything to STOP the steal on 11/03 and now in GEORGIA. She just ask for more money. I stopped all donation to RNC until she is gone
Name Hidden·@Private User5 days ago
Shes a Romney, no place in a conservative party for Romneys
@Name Hidden She is her own person and does not support Uncle Mitt
Name Hidden·@Private User5 days ago
@Ehanda So she says, but where was the RNC through all this? Why are they not making noise right now? why are they not sending people to these polling places and forcing their way in to observe? Why are so many RINOs not supporting election investigations? Because leadership is weak. It starts at the top, and they all gotta go as far as I'm concerned. They do not recognize that trump, NOT Republicans, got 74+ million votes
@Name Hidden I agree with everything you've said, but I've only seen Ronna work hard for our #PresidentTrump
I don't think she's the problem. There are problems, but pointing her out is looking in the wrong place. Look at the dozens of Republicans who never say a word, never speak up in support of #DonaldTrump They are the problem, and need to be voted out and replaced with Republicans on fire for our Republic and #Trump
Name Hidden·@Private User1 day ago
@ConservativoSole Agreed, but she has a lot of pull and Ihaven't heard her say a word about the tech censorship going on right now. In fact, the platform we are currently talking on is getting removed from amazons servers tomorrow, and I hear crickets from the RNC. Shes not terrible, but she's gotta go just like the rest of the RINOs.  In case you guys didn't know, while Trump was fighting the steal back on November, McDaniel was fielding 2024 candidates 😠
And out of the $250 million the GOP hauled in after Nov 3rd, do you all know how much they gave the Trump campaign??? $14 million. She is horrible. Get rid of her 😠😠😠
Arrest her.
Oh why? I like her
Why, though?
She should have been gone long ago
She’s a traitor infiltrator!
Why?
🚨🚨🚨BLM
The entire GOP leadership is corrupt.
I agree on principal, but the truth is the GOP is dead. Long line the Patriot party!
Why?
You cant just put out a statement like thst without giving a reason.
What did ronna McDaniel do
I thought she was on trumps side
Yes
How come?
Why?
She's worthless. In case you guys didn't know, while Trump was fighting the steal back on November, McDaniel was fielding 2024 candidates 😠
And out of the $250 million the GOP hauled in after Nov 3rd, do you all know how much they gave the Trump campaign??? $14 million. She is horrible.
Right there is the problem - ROMNEY
No more division! Ronna is on our side.
She has been silent, absent and completely ineffective.
Second that...its like her job is to sit and do nothing for us.


RightValues
Results
RightValues
(rightvalues.github.io)
Closest Match:
You can send these results by copying and pasting the URL with the button below, or by copying the canvas image in the bottom of the page.
Next closest matches
With your closest match as 100% and farthest as 0%, here is how closely you matched the other ideologies.
I don't like my scores!
Please remember that you are not intended to get a 100% score in any of the categories. The point of the quiz is to challenge your views. If you have any suggestions or constructive criticism please fill out this short form.


6.810 Engineering Interactive Technologies (fall 2021)
Lab 11: Creating the Lamp Shade with Conductive Yarn
In this lab, you will use fabric to make a lamp shade for your 3D printed lamp. After preparing the main fabric, you will sew on touch buttons using conductive yarn. After this, you will mount LEDs into the base of the lamp. In the next lab, we will then write code to light up the lamp in different colors depending on which touch sensor on the lamp shade it touched and also use one touch sensor to turn the lamp on/off.
Steps:
- Laser Cut the Fabric Cover for the Lamp Shade
- Sew Fabric Lamp Shade Cover Together
- Embroider Touch Sensors with Conductive Yarn
- Connect Touch Buttons to Jumper Wires
- Connect LEDs to Lamp Shade
Deliverables
At the end of the lab, upload to your student google drive:
- your laser cut drawing (.pdf)
- 2-3 photos (.jpg or .png) of your assembled lamp showing the stitched touch buttons and integrated LED strip
Help us Improve Class Materials for Lab11:
Please let us know if you had any trouble with the fabrication and assembly or if anything was confusing in the write up.
You can add your comments here.
(1) Laser Cut the Fabric Cover for the Lamp Shade
First, you will create the fabric cover
that will go onto the 3D printed cone of the lamp shade
. To create the fabric cover, you will first laser cut the desired shape and then sew it together.
Draw Fabric Cover Shape for Laser Cutting: Create a new file in Adobe Illustrator
for the laser cut drawing. The lamp shade will be laser cut in the shape of a half circle
with a radius of 14cm
. The easiest way to draw a half circle is to first draw a full circle
in Illustrator (width/height: 28cm)
and then delete half of the circle
with the 'white mousepointer' tool
, which allows you to select individual parts of the circle path. Finally, to close the half circle, draw a straight line
from one end to the other.
Set Laser Cut Settings for your Drawing: Make sure you are using the correct drawing settings for laser cutting (line color: full red (R:255, G:0, B:0)
, and line thickness: 0.0001mm)
.
Laser Cut Fabric: Once you are done making the drawing, export it as a .pdf
and you are ready for laser cutting. We already cut out the fabric patches in a size of 30cmx20cm
, which will fit your drawing. Laser cut your half circle out of this patch (it does not matter which side of the fabric faces up in the laser cutter). When cutting, set the speed to 100%
and the power to 35%
.
(2) Sew Fabric Lamp Shade Cover Together
In the next step, we will fold and then sew together the laser cut piece of fabric.
Fold Fabric, Nice Side Should Face Inside: Next, you need to fold your half circle fabric
over in the middle. Before you fold it, look at the fabric. You will notice that it has a good looking side with the pattern well visible and a less good looking side. After folding, the good looking side should be facing inwards
and the not good looking side should be facing outwards
(yes, this is correct). The reason we are doing this is that sewing is always done on the inside of the final object, so later on we will flip the fabric cover over and the good looking side will be visible outside.
Sew Together Straight Open Edge: Next, sew together the straight edge
of the half circle that is not yet connected
. Take one of our mobile sewing machines
and sew together the open straight line. To do this, insert the fabric into the sewing machine
and then draw the sewing machine in one straight motion
over the edge as shown below. You can start a little bit towards the inside to make the cone more tightly fit on the lamp shade. When you reach the end of the edge, keep the sewing machine running for another 5-10 seconds
. This will add a few more knots to the yarn which will ensure that the yarn does not open up directly when you pull on it accidentally. Carefully disconnect the sewing machine
without untanging the knots you just made.
Glue Yarn to Fix End Point: Use glue
to fix the yarn at the endpoint, so that the thread does not come apart when you handle the lamp shade in the next steps. Wait for the glue to dry
.
Flip Over Fabric Cover: Once the glue has dried, flip over your fabric
so that the good looking side now faces outside. Place it onto your 3D printed lamp shade to see if it fits.
(3) Embroider Touch Sensors with Conductive Yarn
Now that you have the basic fabric cover, you are ready to add the touch buttons
onto the lamp shade using conductive yarn
. We will add 4 touch buttons
to the fabric lamp shade. One of the touch buttons will later be used to turn the lamp on and off
, and the other three touch buttons will be used to set 3 different color schemes
for the lamp (e.g. Red, Green, and Blue).
Insert Conductive Yarn into Needle: Take a needle
and a roll of conductive yarn. Thread the conductive yarn into the needle. Make a double knot at the end of the yarn
to make sure that it will hold onto the fabric and doesn't go through it.
Placement of Buttons: Before stitching the buttons, decide where you want to place them
. In our example, we put the three R, G, B light color buttons on one side
of the lamp and the on/off button on the other side
of the lamp. You need to leave at least 2cm between adjacent touch buttons
otherwise there will be cross talk among the wires of the buttons.
Stitch Touch Buttons as Cross Patterns and Glue Ends to Lampshade: Start with the first button and stitch a cross pattern
into the fabric. Below you see the cross stitch step-by-step.
Use glue to secure the end
of the yarn in place. Then continue with the next touch button. Each touch button should use its own yarn (i.e. wire)
. They should not be connected together with the same yarn since we otherwise would not be able to sense which button is touched. When you start the next touch button, make sure to leave at least 2cm distance
to the previous touch button so there is no cross talk among the wires of the buttons.
(4) Connect Touch Buttons to Jumper Wires
Since we can't stick the conductive yarn into a breadboard, we will connect it to a jumper wire and then use the jumper wire for breadboarding.
Wrap Copper Tape Around Jumper Wire: Get a jumper wire
.  First, wrap the beginning of the copper tape around the jumper wire. Before closing off the tape, also include the conductive yarn so everything is connected together
.
Repeat for All Conductive Yarns: Repeat these steps for all 4 conductive yarns
connected to our touch buttons. At the end of this step, you should have four jumper wires connected ready for breadboarding.
Place Fabric Cover on 3D printed Lamp & Glue Wires onto Arms of Lamp Shade: You are now ready to place the fabric over the 3D printed lamp shade. Make sure that the conductive yarns inside the lamp shade do not touch each other
. You can ensure this by glueing each jumper wire to one of the arms
of the lamp shade to spread them out.
(5) Connect LEDs to Lamp Shade
Next, you will connect the LEDs
to your lamp shade. For the LEDs, we will use Neopixel LEDs
, which are RGB LEDs that can be addressed individually
and thus each LED on the strip can take on any color you like.
 As you can see in the image below, between each LED there are 3 copper pads
. You need to cut off a strip of 4 LEDs
while making sure you cut the copper pads right in the middle at the end of your strip
, so the next person still has enough copper pads on their LEDs to be able to solder on a wire.
Remove Silicone from End of LED Strip: There is a silicone film
that covers the LED strip. The silicone film is used to protect the LED strip from water
and other environmental influences but prevents us from soldering
onto the copper pads. We only need to solder at the end of the LED strip, but which end should we use?  There are arrows on it. We want to solder
on the side where the arrows point away from
. Remove the silicone film from the top of the LED strip on this side.
Solder Jumper Wires onto Copper Pads: To control the LEDs colors, we need some wires that go from the LED copper pads
to the microcontroller. Note that the Neopixel LED strip is setup so that you only have to connect to the copper pads of a single LED in order to be able to set the color of each individual LED on the entire strip (more on this in the next lab). Thus, you only need to solder three jumper wires
to the end of the strip where you just removed the silicone. For soldering, first put a solder drop on each pad
and then press the tip of the jumper wire on the drop
with the solder iron.
Attach the LEDs onto the Lamp Shade: Remove the blue foil
from back of the LED stripes. Underneath the blue foil, there is double sided tape on the back of the LED strip
. With the double sided tape exposed, you can now attach the LEDs to one of the free arms inside the printed lamp shade (i.e., an arm that does not have a jumper wire connected to a touch button).
Deliverables
At the end of the lab, upload to your student google drive:
- your laser cut drawing (.pdf)
- 2-3 photos (.jpg or .png) of your assembled lamp showing the stitched touch buttons and integrated LED strip
Please let us know if you had any trouble with the fabrication and assembly or if anything was confusing in the write up.
You can add your comments here.


Welcome to the Beta Launch of ‘Joyful & Euphoric’ - We are building a companion and client booking service based in New York City.
" My name is ‘Ella’, and I’m passionate about arranging relaxing, safe, and discreet dates. To ensure ‘Joyful’ is prepared to anticipate this community's needs, I’ve created two short surveys: One for companions, and a second for clients. I’d appreciate your feedback!" - Ella
"Joyful makes sure having fun stays simple!"
Finally outsource all of your booking, screening, advertising, even bookkeeping, and as much client communication as you'd like...all while retaining your independence!
Joyful & Euphoric offers a client hotline and chat support during office hours to respond to inquiries in real time.
An uncompromising screening process, backed up by rigorous safety practices.
...and for clients, screen once and meet multiple companions for as long as you maintain your complimentary partnership credentials.
Photo editing - and eventually, full-service professional photoshoots - fully covered by Joyful.
Complimentary supplies and partial reimbursements on multiple business expenses.
We don't rely on marketing "in bulk" - Joyful develops a hyper-personalized branding strategy, led by you.
Oh, and it's actually affordable!
We operate on a commission structure outlined below, with a simple two-catgory system: Joyful Companions have a one-hour minimum, and Euphoric Companions have the same structure with a multi-hour minimum.
Our core promise: Joyful is only successful when you are successful.
With Joyful & Euphoric, both client and companion can relax knowing all fees are all-inclusive.
Sounds interesting? Complete either survey above and we'll be sure to follow up!
(Other questions, or need to report site issues? Email me at<EMAIL_ADDRESS>- Joyful & Euphoric is worker-led, and supports #DecrimNYC -


Find contact information for faculty, staff and students. Search by:
Find UNL departments by entering a full or parital department name. Information available:
Information obtained from this directory may not be used to provide addresses for mailings to students, faculty or staff. Any solicitation of business, information, contributions or other response from individuals listed in this publication by mail, telephone or other means is forbidden.


Adventures of Huckleberry Finn (or, in more recent editions, The Adventures of Huckleberry Finn) is a novel by Mark Twain, first published in the United Kingdom in December 1884 and in the United States in February 1885. Commonly named among the Great American Novels, the work is among the first in major American literature to be written throughout in vernacular English, characterized by local color regionalism. It is told in the first person by Huckleberry "Huck" Finn, the narrator of two other Twain novels (Tom Sawyer Abroad and Tom Sawyer, Detective) and a friend of Tom Sawyer. It is a direct sequel to The Adventures of Tom Sawyer.
Price = {{ price | currency : "Rs."}}


{% extends 'bootstrap/base.html' %} {% import "bootstrap/wtf.html" as wtf %} {% block content %} {% include "header.html" %}
Please login for content to be displayed
Start creating your budget


twitter-stream-channels Module
Summary
With the Twitter stream API, you can only open one stream at a time. So, if you have multiple filters, the tweets in result will be mixed up, you'll need to do some post-processing.
This module lets you open multiple channels with there own filters, on the same stream. And then, you can add events to each of them individually, to listen to there results, like if you had open multiple streams.
twitter-stream-channels handles all the post-processing, the connexion layer (OAuth, etc ...) is handled by twit. See the FAQ about both topics.
This module provides the following classes:


--- date: 2010-02-04T00:00:00Z published: true title: Give Danny Williams a break. No way. url: /2010/02/04/-give-danny-williams-a-break-no-way-/ ---
The main criticism dogging Williams, of course, is while buying health care is fine for a rich person, what about those Canadians who can't afford to leave medicare coverage behind, and jet off to the U.S. on a wing and a prayer and an Amex card with no limit?
The short answer is by going to the U.S., Williams is actually saving Canadian taxpayers money, while taking himself out of the lineup for treatment covered by medicare.
Cheaper for taxpayers. Faster treatment for those who rely on medicare. Hard to argue.
This is the core of my issue with this.
I believe that it's nobody else's business what I spend my after tax dollars on - if I want to pay for a medically necessary surgery, I should be able to.
But Canadian politicians have deemed that conversation (and option) out of bounds. That's when people like Danny boy start to channel the dead soul of Tommy Douglas and declare their worry about the state of Canadian medicine. All the while, they do the exact same thing - "linups for thee, but not for me".
The truly rich in Canada can bypass any line or waiting list - but the rest of us have to pay our taxes and our government health care costs and get sub-standard treatment.
Cowards and hypocrites, the lot of 'em.


Stones grid examples
Basic grid layouts to get you familiar with building within the Bootstrap grid system.
Three equal columns
Get three equal-width columns starting at desktops and scaling to large desktops. On mobile devices, tablets and below, the columns will automatically stack.
.column .small-4
.column .small-4
.column .small-4
Three unequal columns
Get three columns starting at desktops and scaling to large desktops of various widths.
Remember, grid columns should add up to twelve for a single horizontal block. More than that, and columns start stacking no matter the viewport.
.column .small-3
.column .small-6
.column .small-3
Two columns
Get two columns starting at desktops and scaling to large desktops.
.column .small-8
.column .small-4
Full width, single column
No grid classes are necessary for full-width elements.
Two columns with two nested columns
Per the documentation, nesting is easy—just put a row of columns within an existing column. This gives you two columns starting at desktops and scaling to large desktops, with another two (equal widths) within the larger column.
At mobile device sizes, tablets and down, these columns and their nested columns will stack.
.column .small-8
.column .small-6
.column .small-6
.column .small-4
Mixed: mobile and desktop
The Bootstrap 3 grid system has four tiers of classes: xs (phones), sm (tablets), md (desktops), and lg (larger desktops).
You can use nearly any combination of these classes to create more dynamic and flexible layouts.
.column .small-12 .medium-8
.column .small-6 .medium-4
.column .small-6 .medium-4
.column .small-6 .medium-4
.column .small-6 .medium-4
.column .small-6
.column .small-6
Column clearing
Clear floats at specific breakpoints to prevent awkward wrapping with uneven content.
.column .small-6 .medium-3
Resize your viewport or check it out on your phone for an example.
.column .small-6 .medium-3
.column .small-6 .medium-3
.column .small-6 .medium-3


Tesserae
Animated mosaic backgrounds with HTML5 Canvas
Hue
random
red
orange
yellow
green
blue
purple
pink
monochrome
Luminosity
bright
light
dark
Tessera size
Filter
Draw
Gradual
Simultaneous
Animation
Enabled
Disabled
Live mode
Enabled
Disabled
Live min interval
Live max interval
Draw step
Animate step


Hello wonderful people! It's Friday again and you all know what this means ⚛.
🔥 React.warn() and React.error()
This PR adds two new APIs to React: React.warn() and React.error(). These will log to the console and include a component stack. Useful for providing more detailed error messages in third party packages.
Module pattern components take a fairly complex code path in React but are barely used outside of a few special places (like Relay Classic). This PR deprecates this pattern as also outlined in the RFC about createElement changes.
⚠ ESLint: Warn About setState Directly in Dep-less useEffect
The ESLint rule for hooks now warns when setState is called without defining dependencies. This could easily lead to loops. This change is already out in<EMAIL_ADDRESS>React uses invariant() to assert correct behavior. In production, the error message is stripped out to keep the bundle size down. This PR reworks how this is handled.
While working on internal experiments for a new event API, the Core team added more scaffolding, a new react-events package, and logic for listening to event responders.


Elizabeth Warren Salutes Vets in New Policy Pitch
by AMANDA OTTAWAY(CN) – Ahead of the Veterans Day holiday, Democratic presidential hopeful Elizabeth Warren released a new plan to take care of U.S. servicemembers. This plan is personal for her, she said: Three of her brothers served.
“Parades and salutes to the troops are important ways that Americans express their gratitude, but they’re only platitudes if they’re not backed up with meaningful action and policies that support our military both during and after service — not just on Veterans Day, but every day,” the Massachusetts senator wrote in her latest online policy announcement.
Warren said her first focus will be to take care when considering deploying troops abroad, citing the wars in Iraq and Afghanistan that have stretched nearly two decades, killed more than 7,000 U.S. troops and injured almost 60,000 more. A strong military should be a deterrent first, Warren said.
She also slammed the U.S. defense budget as “bloated” and said defense contractors have too much sway in Washington.
“It’s not fair to our men and women in uniform to ask them to solve problems that don’t have a military solution,” she wrote.
Warren also said the military needs to offer better pay and benefits and otherwise compete better with the civilian jobs sector for the country’s best and brightest. Touting her Consumer Financial Protection Bureau, which she said helps protect servicemembers’ families from predatory loan practices, Warren said she would expand its practices and authority. Another service Warren called to expand is education benefits for service members and their families.
She said she would work as well to support military spouses, 30% of whom were unemployed last year. And she would back a universal credentialing system for professionally licensed employees, noting the challenges they face when they move from state to state for their spouse’s military job.
Warren said her plan for universal child care will also help military families and employment, and that she will invest in modernizing schools and improving daycares on military bases.
Noting that 30% of vets between 21 and 64 have a disability, Warren also promised to protect disabled veterans at work and support their caregivers with her Medicare for All And expanded Social Security plans.
Warren wants better quality military housing as well, to end veteran homelessness and give tenants access to advocates And the Bill of Rights. She also promised to reform the justice process for military sexual assault and support survivors.
“Until reporting an assault is not perceived as a possible end to someone’s career, we will never fully address this scourge,” Warren wrote.
With a first-term goal of halving the number of vets who die by suicide, Warren additionally promised better mental health care for service members and veterans.
In another nod to equal protection for all service members, Warren said she would repeal Trump’s ban on transgender service numbers her first day in office.
She also promised to revitalize the VA.
“The way I see it, this is not complicated. It’s about a government that keeps its promises to those who served — it’s about our values,” Warren wrote.
Copyright © 2019 courthousenews.com


At midday, greased and pungent, the village wrestler took up his position by the war memorial. With his aggressive stance and tiny brain, he challenged everyone passing by to a wrestling bout. But not one among the villagers was fool enough to fight him and so, in his frustration, he would fling himself upon a dog or a chicken or a defenceless toddler straying from its mother's apron-strings, and wrestle them to the ground and pin them there, counting to ten in awful grunts, and then stand up, arms raised aloft in victory.
 He lay there unconscious for days. The villagers debated what to do with him when he awoke. Chains and cages were suggested, as was electro-convulsive therapy. But then, when he did awake, the village wrestler was placid, all aggression spent. He took a chicken and a flask of water drawn from the village trough, and he wandered off along the lane that led to the aerodrome. We never saw him again.


Choose the pizza of your choice, we make it for you and we serve it to you where you are.Share a pizza, share happiness!
Cost per one pizza:
Total cost of your order:
Here is the order you placed:
Copyright © 2020 PizzaGustosa


Controls
play
playHold
Random Mutate
split Mutate
add Oscillator
Add Connection
Mutate Weights
Mutate Node parameter
play Clone
Current Instrument toString
Current Instrument toJSON
Log


"Meet Trackin, a complete software solution for restaurant managers to control your fleet of delivery persons in real time and easily accept online orders. Trackin provides a dashboard for your restaurants, a mobile app for your drivers and an online order widget for your customers. Thanks to this startup attending Y Combinator’s current batch, you will know when your driver is back and when to start cooking.
“With Trackin, you can track your drivers like in Uber’s app, and your clients can do the same as well,” founder and CEO Bruno Didier told me in a phone interview. “We can tell you what are your best delivery zones, and we’re a white label service.”
The dashboard centralizes all your orders, including on third-party websites. People can order on your website using Trackin’s online order widget, GrubHub, Eat24 and other online ordering platforms, and every order will end up in Trackin. And of course, if someone calls you, you can manually add an order to your dashboard."


Relatively Simple teaches you about Einstein's theory of special relatively.
Designed by Prof. Ira Rothstein and developed in conjunction with students at CMU's Entertainment Technology Center.
3 Different Sandboxes
Each teaches you a new aspect of Special Relativity!
See how the length of object changes as you move with respect to it.
See how it can be that one observer thinks two events are occurring at the same time but another (you) thinks otherwise.
Learn how, as you move faster, time passes more slowly for you at least when compared with the time of an inertial observer or someone at rest relative to you.


 Aenean fermentum dolor augue, id tincidunt dolor faucibus ut. In a pharetra est.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean fermentum dolor augue, id tincidunt dolor faucibus ut.
In a pharetra est. Mauris malesuada urna sit amet magna pharetra, sed porta urna interdum. MorbiEtiam egestas.Mauris malesuadasd amet magna.Lorem ipsum dolor sit amet
Read MoreView our Exclusive
Besca is a global manufacturer of the electrical and mechanical infrastructure utilized within the oil and gas market?
Lorem Ipsum is simply dummy text of the priting and typesetting industry. Lorem Ipsum has been the industry.
Lorem Ipsum is simply dummy text of the priting and typesetting industry. Lorem Ipsum has been the industry.
Lorem Ipsum is simply dummy text of the priting and typesetting industry. Lorem Ipsum has been the industry.
Lorem Ipsum is simply dummy text of the priting and typesetting industry. Lorem Ipsum has been the industry.
Mill Steel is defined as steel without treatment. Same as “black steel”. It means that no surface finish was treated on the steel..


I'm a Computer Science Graduate student at University at Buffalo. I am passionate about the field of Artificial Intelligence and its application in Computer Vision and Data Science. Previously I have worked as a Software Engineer at Sapient Global Markets, India. My primary role there was to design and develop scripts in Java to improve existing functionalities. It helped me learn the technical aspects of software development as well as ethics of corporate work culture.
• Developed and implemented several Java based applications for data extraction, automate trade capture and perform trade calculations with Endur an energy trading and risk management tool by OpenLink.
• Worked on critical defects, production bug fixing, automating daily activities and improving the efficiency of the system as part of monthly maintenance releases.
• Worked with clients on gathering requirements, created technical document, wrote functional test cases and deployment documents.
• Developed good understanding of database by analysing and optimizing existing SQL scripts as well as writing new ones.