Example #1
0
exports.fastForwardMerge = co.wrap(function *(repo, mode, commit, message) {
    assert.instanceOf(repo, NodeGit.Repository);
    assert.isNumber(mode);
    assert.instanceOf(commit, NodeGit.Commit);
    assert.isString(message);

    // Remember the current branch; the checkoutCommit function will move it.

    const branch = yield repo.getCurrentBranch();
    let result = null;
    let newHead;
    if (MODE.FORCE_COMMIT !== mode) {
        // If we're not generating a commit, we just need to checkout the one
        // we're fast-forwarding to.

        yield Checkout.checkoutCommit(repo, commit, false);
        newHead = commit;
    }
    else {
        // Checkout the commit we're fast-forwarding to.

        const head = yield repo.getHeadCommit();
        yield Checkout.checkoutCommit(repo, commit, false);

        // Then, generate a new commit that has the previous HEAD and commit to
        // merge as children.

        const sig = repo.defaultSignature();
        const tree = yield commit.getTree();
        const id = yield NodeGit.Commit.create(
                                           repo,
                                           0,
                                           sig,
                                           sig,
                                           null,
                                           Commit.ensureEolOnLastLine(message),
                                           tree,
                                           2,
                                           [head, commit]);
        newHead = yield repo.getCommit(id);
        result = newHead.id().tostrS();

        // Move HEAD to point to the new commit.

        yield NodeGit.Reset.reset(repo,
                                  newHead,
                                  NodeGit.Reset.TYPE.HARD,
                                  null,
                                  branch.name());
    }

    // If we were on a branch, make it current again.

    if (branch.isBranch()) {
        yield branch.setTarget(newHead, "ffwd merge");
        yield repo.setHead(branch.name());
    }
    return result;
});
Example #2
0
    const writeCommit = co.wrap(function *(sha) {
        const commit = commits[sha];
        const parents = commit.parents;

        // Get commit objects for parents.

        let parentTrees;
        let newParents = [];  // Array of commit IDs
        for (let i = 0; i < parents.length; ++i) {
            const parent = parents[i];
            if (0 === i) {
                parentTrees = treeCache[parent];
            }
            const parentSha = oldCommitMap[parent];
            const parentCommit = yield repo.getCommit(parentSha);
            newParents.push(parentCommit);
        }

        // Calculate the tree.  `trees` describes the directory tree specified
        // by the commit at `sha` and has caches for subtrees and submodules.

        const trees = yield writeTree(repo,
                                      oldCommitMap,
                                      commit.changes,
                                      parentTrees);

        // Store the returned tree information for potential use by descendants
        // of this commit.

        treeCache[sha] = trees;

        // Make a commit from the tree.

        const commitId = yield NodeGit.Commit.create(repo,
                                                     0,
                                                     sig,
                                                     sig,
                                                     0,
                                                     commit.message,
                                                     trees.tree,
                                                     newParents.length,
                                                     newParents);
        const commitSha = commitId.tostrS();

        // Store bi-directional mappings between generated and logical sha.

        oldCommitMap[sha] = commitSha;
        newCommitMap[commitSha] = sha;
        commitObjs[commitSha] = (yield repo.getCommit(commitSha));
        return commitSha;
    });
Example #3
0
 const commitAndBranch = co.wrap(function *(treeId, type) {
     const tree = yield NodeGit.Tree.lookup(repo, treeId);
     const commitId = yield NodeGit.Commit.create(repo,
                                                  null,
                                                  sig,
                                                  sig,
                                                  null,
                                                  type,
                                                  tree,
                                                  0,
                                                  []);
     const commit = yield repo.getCommit(commitId);
     yield NodeGit.Branch.create(repo, type, commit, 1);
     commitMap[commitId.tostrS()] = type;
 });
Example #4
0
exports.writeNotes = co.wrap(function *(repo, refName, contents) {
    assert.instanceOf(repo, NodeGit.Repository);
    assert.isString(refName);
    assert.isObject(contents);

    if (0 === Object.keys(contents).length) {
        // Nothing to do if no contents; no point in making an empty commit or
        // in making clients check themselves.
        return;                                                       // RETURN
    }

    // We're going to directly write the tree/commit for a new note containing
    // `contents`.

    let currentTree = null;
    const parents = [];
    const ref = yield GitUtil.getReference(repo, refName);
    if (null !== ref) {
        const currentCommit = yield repo.getCommit(ref.target());
        parents.push(currentCommit);
        currentTree = yield currentCommit.getTree();
    }
    const odb = yield repo.odb();
    const changes = {};
    const ODB_BLOB = 3;
    const BLOB = NodeGit.TreeEntry.FILEMODE.BLOB;
    const writeBlob = co.wrap(function *(sha) {
        const content = contents[sha];
        const blobId = yield odb.write(content, content.length, ODB_BLOB);
        const sharded = exports.shardSha(sha);
        changes[sharded] = new TreeUtil.Change(blobId, BLOB);
    });
    yield DoWorkQueue.doInParallel(Object.keys(contents), writeBlob);

    const newTree = yield TreeUtil.writeTree(repo, currentTree, changes);
    const sig = yield ConfigUtil.defaultSignature(repo);
    const commit = yield NodeGit.Commit.create(repo,
                                               null,
                                               sig,
                                               sig,
                                               null,
                                               "git-meta updating notes",
                                               newTree,
                                               parents.length,
                                               parents);
    yield NodeGit.Reference.create(repo, refName, commit, 1, "updated");
});
Example #5
0
exports.save = co.wrap(function *(repo, status, includeUntracked, message) {
    assert.instanceOf(repo, NodeGit.Repository);
    assert.instanceOf(status, RepoStatus);
    assert.isBoolean(includeUntracked);
    if (null !== message) {
        assert.isString(message);
    }

    const subResults = {};  // name to sha
    const subChanges = {};  // name to TreeUtil.Change
    const subRepos   = {};  // name to submodule open repo

    const sig = repo.defaultSignature();

    // First, we process the submodules.  If a submodule is open and dirty,
    // we'll create the stash commits in its repo, populate `subResults` with
    // the `Stash.Submodule` that will be returned, `subChanges` with the sha
    // of the commit to be made to be used in generating the new submodule
    // tree, and `subRepos` to cache the open repo for each sub to be used
    // later.

    const submodules = status.submodules;
    yield Object.keys(submodules).map(co.wrap(function *(name) {
        const sub = submodules[name];
        const wd = sub.workdir;
        if (null === wd ||
            (wd.status.isClean() &&
             (sub.commit === null ||
              wd.status.headCommit === sub.commit.sha) &&
             (!includeUntracked ||
              0 === Object.keys(wd.status.workdir).length))) {
            // Nothing to do for closed or clean subs

            return;                                                   // RETURN
        }
        const subRepo = yield SubmoduleUtil.getRepo(repo, name);
        subRepos[name] = subRepo;

        let stashId;
        if (sub.commit !== null && wd.status.headCommit === sub.commit.sha) {
            const FLAGS = NodeGit.Stash.FLAGS;
            const flags = includeUntracked ?
                  FLAGS.INCLUDE_UNTRACKED :
                  FLAGS.DEFAULT;
            stashId = yield NodeGit.Stash.save(subRepo, sig, "stash",
                                                     flags);
        }
        else {
            stashId = NodeGit.Oid.fromString(wd.status.headCommit);
        }
        subResults[name] = stashId.tostrS();
        // Record the values we've created.

        subChanges[name] = new TreeUtil.Change(
                                            stashId,
                                            NodeGit.TreeEntry.FILEMODE.COMMIT);
    }));
    const head = yield repo.getHeadCommit();
    const headTree = yield head.getTree();
    const subsTree = yield TreeUtil.writeTree(repo, headTree, subChanges);
    const stashId = yield NodeGit.Commit.create(repo,
                                                null,
                                                sig,
                                                sig,
                                                null,
                                                "stash",
                                                subsTree,
                                                1,
                                                [head]);

    const stashSha = stashId.tostrS();

    // Make synthetic-meta-ref style refs for sub-repos.

    yield Object.keys(subRepos).map(co.wrap(function *(name) {
        const sha = subResults[name];
        const refName = makeSubRefName(sha);
        yield NodeGit.Reference.create(subRepos[name],
                                       refName,
                                       sha,
                                       1,
                                       "sub stash");
    }));

    // Update the stash ref and the ref log

    if (null === message) {
        message = yield exports.makeLogMessage(repo);
    }
    yield NodeGit.Reference.create(repo,
                                   metaStashRef,
                                   stashId,
                                   1,
                                   message);

    yield exports.createReflogIfNeeded(repo, metaStashRef, stashSha, message);
    return subResults;
});
Example #6
0
exports.writeStitchedCommit = co.wrap(function *(repo,
                                                 commit,
                                                 subChanges,
                                                 parents,
                                                 keepAsSubmodule,
                                                 adjustPath,
                                                 skipEmpty,
                                                 whitelist) {
    assert.instanceOf(repo, NodeGit.Repository);
    assert.instanceOf(commit, NodeGit.Commit);
    assert.isObject(subChanges);
    assert.isArray(parents);
    assert.isFunction(keepAsSubmodule);
    assert.isFunction(adjustPath);
    assert.isBoolean(skipEmpty);
    assert.instanceOf(whitelist, Set);

    let updateModules = false;  // if any kept subs added or removed
    const changes = {};         // changes and additions
    let subCommits = {};        // included submodule commits
    const stitchSub = co.wrap(function *(name, oldName, sha) {
        let subCommit;
        try {
            subCommit = yield repo.getCommit(sha);
        }
        catch (e) {
            const metaSha = commit.id().tostrS();
            if (whitelist.has(metaSha)) {
                return;                                               // RETURN
            }
            throw new UserError(`\
On meta-commit ${metaSha}, ${name} is missing ${sha}.
To add to allow this submodule change to be skipped, run:
git notes --ref ${exports.whitelistNoteRef} add -m skip ${metaSha}`);
        }
        const subTreeId = subCommit.treeId();
        changes[name] = new TreeUtil.Change(subTreeId, FILEMODE.TREE);

        // Now, record this submodule change as introduced by this commit,
        // unless it already existed in another of its parents, i.e., it was
        // merged in.

        const alreadyExisted =
                yield exports.sameInAnyOtherParent(repo, commit, oldName, sha);
        if (!alreadyExisted) {
            subCommits[name] = subCommit;
        }
    });

    function changeKept(name, newSha) {
        const id = NodeGit.Oid.fromString(newSha);
        changes[name] = new TreeUtil.Change(id, FILEMODE.COMMIT);
    }

    for (let name in subChanges) {
        const mapped = adjustPath(name);
        if (null === mapped) {
            continue;                                               // CONTINUE
        }
        const newSha = subChanges[name].newSha;
        changes[mapped] = null;

        if (keepAsSubmodule(name)) {
            updateModules = true;
            if (null !== newSha) {
                changeKept(name, newSha);
            }
        } else if (null !== newSha) {
            yield stitchSub(mapped, name, newSha);
        }
    }

    // If any kept submodules were added or removed, rewrite the modules
    // file.

    if (updateModules) {
        const newUrls =
               yield SubmoduleConfigUtil.getSubmodulesFromCommit(repo, commit);
        const content = yield exports.computeModulesFile(repo,newUrls,
                                                         keepAsSubmodule,
                                                         adjustPath);
        changes[SubmoduleConfigUtil.modulesFileName] = content;
    }

    let newCommit = null;

    if (!skipEmpty || 0 !== Object.keys(changes).length) {
        // If we've got changes or are not skipping commits, we make one.

        let parentTree = null;
        if (0 !== parents.length) {
            const parentCommit = parents[0];
            parentTree = yield parentCommit.getTree();
        }

        const newTree = yield TreeUtil.writeTree(repo, parentTree, changes);

        const commitMessage = exports.makeStitchCommitMessage(commit,
                                                              subCommits);
        const newCommitId = yield NodeGit.Commit.create(
                                                      repo,
                                                      null,
                                                      commit.author(),
                                                      commit.committer(),
                                                      commit.messageEncoding(),
                                                      commitMessage,
                                                      newTree,
                                                      parents.length,
                                                      parents);
        newCommit = yield repo.getCommit(newCommitId);
    } else if (0 !== parents.length) {
        // If we skip this commit, map to its parent to indicate that whenever
        // we see this commit in the future, substitute its parent.

        newCommit = parents[0];
        subCommits = {};
    }
    return {
        stitchedCommit: newCommit,
        subCommits: subCommits,
    };
});