.then(function(_commit) { commit = _commit; var work; switch (rebaseOperation) { case "ABORT": work = git.Rebase.open(repo, {}).then(function(rebase){ return rebase.abort(); }); break; case "CONTINUE": work = repo.continueRebase(); break; case "SKIP": return git.Checkout.head(repo, { checkoutStrategy: git.Checkout.STRATEGY.FORCE | git.Checkout.STRATEGY.RECREATE_MISSING | git.Checkout.STRATEGY.REMOVE_UNTRACKED }) .then(function() { return git.Rebase.open(repo, {}); }).then(function(rebase) { if (rebase.operationCurrent() === rebase.operationEntrycount() - 1) { // if skipping the last operation, then we're done here rebase.finish(repo.defaultSignature()); // return the commit that we're currently on return head; } // move to the next one and continue return rebase.next().then(function(rebaseOperation) { return repo.continueRebase(); }); }); default: work = repo.rebaseBranches("HEAD", commitToRebase, null, null, null); } return work .then(function(_oid) { oid = _oid; }) .catch(function(index) { if (rebaseOperation === "ABORT" || !index.entries) { throw index; } paths = {}; index.entries().forEach(function(entry) { if (git.Index.entryIsConflict(entry)) { paths[entry.path] = ""; } }); return git.Checkout.index(repo, index, { checkoutStrategy: git.Checkout.STRATEGY.ALLOW_CONFLICTS, ourLabel: "HEAD", theirLabel: commit.sha() }); }); })
.then(function(_commit) { commit = _commit; var work; switch (rebaseOperation) { case "ABORT": work = git.Rebase.open(repo, {}).then(function(rebase){ return rebase.abort(); }); break; case "CONTINUE": work = repo.continueRebase(); break; case "SKIP": throw new Error("Not implemented yet"); default: work = repo.rebaseBranches("HEAD", commitToRebase, null, null, null); } return work .then(function(_oid) { oid = _oid; }) .catch(function(index) { if (rebaseOperation === "ABORT" || !index.entries) { throw index; } paths = {}; index.entries().forEach(function(entry) { if (git.Index.entryIsConflict(entry)) { paths[entry.path] = ""; } }); return git.Checkout.index(repo, index, { checkoutStrategy: git.Checkout.STRATEGY.ALLOW_CONFLICTS, ourLabel: "HEAD", theirLabel: commit.sha() }); }); })
it("callNext", co.wrap(function *() { const init = "S:C2-1;Bmaster=2;C3-1;Bfoo=3"; const written = yield RepoASTTestUtil.createRepo(init); const repo = written.repo; const ontoSha = written.oldCommitMap["3"]; const fromId = NodeGit.Oid.fromString(ontoSha); const fromAnnotated = yield NodeGit.AnnotatedCommit.lookup(repo, fromId); const head = yield repo.head(); const ontoAnnotated = yield NodeGit.AnnotatedCommit.fromRef(repo, head); const rebase = yield NodeGit.Rebase.init(repo, fromAnnotated, ontoAnnotated, null, null); const first = yield SubmoduleRebaseUtil.callNext(rebase); assert.equal(first.id().tostrS(), ontoSha); const second = yield SubmoduleRebaseUtil.callNext(rebase); assert.isNull(second); }));
const op = co.wrap(function *(repos, maps) { const repo = repos.x; const headCommit = yield repo.getHeadCommit(); const AnnotatedCommit = NodeGit.AnnotatedCommit; const headAnnotated = yield AnnotatedCommit.lookup( repo, headCommit.id()); const targetCommitSha = maps.reverseCommitMap.r; const targetCommit = yield repo.getCommit(targetCommitSha); const targetAnnotated = yield AnnotatedCommit.lookup( repo, targetCommit.id()); const rebase = yield NodeGit.Rebase.init(repo, targetAnnotated, headAnnotated, null, null); const op = yield SubmoduleRebaseUtil.callNext(rebase); const result = yield SubmoduleRebaseUtil.processRebase(repo, rebase, op); if (null === c.conflictedCommit) { assert.isNull(result.conflictedCommit); } else { assert.equal( result.conflictedCommit, maps.reverseCommitMap[c.conflictedCommit]); } const commitMap = {}; Object.keys(result.commits).forEach(newSha => { const oldSha = result.commits[newSha]; const oldLogicalCommit = maps.commitMap[oldSha]; commitMap[newSha] = oldLogicalCommit + "r"; }); return { commitMap: commitMap, }; });
const configureRepo = co.wrap(function *(repo, ast, commitMap, treeCache) { const makeConflictEntry = co.wrap(function *(data) { assert.instanceOf(repo, NodeGit.Repository); if (null === data) { return null; } if (data instanceof RepoAST.Submodule) { //TODO: some day support putting conflicts in the .gitmodules file. assert.equal("", data.url, "submodule conflicts must have empty URL"); const sha = commitMap[data.sha]; return new ConflictUtil.ConflictEntry(FILEMODE.COMMIT, sha); } const id = yield GitUtil.hashObject(repo, data.contents); const mode = data.isExecutable ? FILEMODE.EXECUTABLE : FILEMODE.BLOB; return new ConflictUtil.ConflictEntry(mode, id.tostrS()); }); const makeRef = co.wrap(function *(name, commit) { const newSha = commitMap[commit]; const newId = NodeGit.Oid.fromString(newSha); return yield NodeGit.Reference.create(repo, name, newId, 0, "made ref"); }); let newHeadSha = null; if (null !== ast.head) { newHeadSha = commitMap[ast.head]; } // Then create the refs for (let ref in ast.refs) { yield makeRef("refs/" + ref, ast.refs[ref]); } // Handle remotes. for (let remoteName in ast.remotes) { const remote = ast.remotes[remoteName]; yield NodeGit.Remote.create(repo, remoteName, remote.url); // Explicitly create the desired refs for the remote. for (let branchName in remote.branches) { yield makeRef(`refs/remotes/${remoteName}/${branchName}`, remote.branches[branchName]); } } // Then create the branches we want. for (let branch in ast.branches) { const astBranch = ast.branches[branch]; const ref = yield makeRef("refs/heads/" + branch, astBranch.sha); if (null !== astBranch.tracking) { yield NodeGit.Branch.setUpstream(ref, astBranch.tracking); } } // Deal with bare repos. if (ast.bare) { if (null !== ast.currentBranchName) { yield repo.setHead("refs/heads/" + ast.currentBranchName); } else { repo.setHeadDetached(newHeadSha); } } else if (null !== ast.currentBranchName || null !== ast.head) { // If we use NodeGit to checkout, it will not respect the // sparse-checkout settings. if (null === ast.currentBranchName) { repo.detachHead(); } const toCheckout = ast.currentBranchName || newHeadSha; const checkoutStr = `\ git -C '${repo.workdir()}' checkout ${toCheckout} `; try { yield exec(checkoutStr); } catch (e) { // This can fail if there is no .gitmodules file to checkout and // it's sparse. Git will complain that it cannot do the checkout // because the worktree is empty. } } const notes = ast.notes; const sig = repo.defaultSignature(); for (let notesRef in notes) { const commits = notes[notesRef]; for (let commit in commits) { const message = commits[commit]; yield NodeGit.Note.create(repo, notesRef, sig, sig, commitMap[commit], message, 0); } } if (!ast.bare) { let indexHead = ast.head; // Set up a rebase if there is one, this has to come right before // setting up the workdir, otherwise the rebase won't be allowed to // start. if (null !== ast.rebase) { const rebase = ast.rebase; const originalSha = commitMap[rebase.originalHead]; const ontoSha = commitMap[rebase.onto]; const original = yield NodeGit.AnnotatedCommit.lookup(repo, originalSha); const onto = yield NodeGit.AnnotatedCommit.lookup(repo, ontoSha); // `init` creates the rebase, but it's not actually started (some // files are not made) until the first call to `next`. const rb = yield NodeGit.Rebase.init(repo, original, onto, null, null); yield rb.next(); const gitDir = repo.path(); const rbDir = yield RebaseFileUtil.findRebasingDir(gitDir); const headNamePath = path.join(gitDir, rbDir, RebaseFileUtil.headFileName); yield fs.writeFile(headNamePath, rebase.headName + "\n"); // Starting a rebase will change the HEAD If we render the index // against `ast.head`, it will be incorrect; we must adjust so that // we render against the new head, `onto`. indexHead = rebase.onto; } // Write out sequencer state if there is one. const sequencer = ast.sequencerState; if (null !== sequencer) { const mapped = SequencerStateUtil.mapCommits(sequencer, commitMap); yield SequencerStateUtil.writeSequencerState(repo.path(), mapped); } // Set up the index. We render the current commit and apply the index // on top of it. let indexParent; if (null !== indexHead) { indexParent = treeCache[indexHead]; } const trees = yield writeTree(repo, commitMap, ast.index, indexParent); const index = yield repo.index(); const treeObj = trees.tree; yield index.readTree(treeObj); for (let filename in ast.index) { const data = ast.index[filename]; if (data instanceof RepoAST.Conflict) { const ancestor = yield makeConflictEntry(data.ancestor); const our = yield makeConflictEntry(data.our); const their = yield makeConflictEntry(data.their); const conflict = new ConflictUtil.Conflict(ancestor, our, their); yield ConflictUtil.addConflict(index, filename, conflict); } } yield index.write(); // TODO: Firgure out if this can be done with NodeGit; extend if // not. I didn't see anything about `clean` and `Checkout.index` // didn't seem to work.. const filesStr = ast.sparse ? "-- .gitmodules" : "-a"; const checkoutIndexStr = `\ git -C '${repo.workdir()}' checkout -- git -C '${repo.workdir()}' clean -f -d git -C '${repo.workdir()}' checkout-index -f ${filesStr} `; yield exec(checkoutIndexStr); // Now apply changes to the workdir. const workdir = ast.workdir; for (let filePath in workdir) { const change = workdir[filePath]; const absPath = path.join(repo.workdir(), filePath); if (null === change) { yield fs.unlink(absPath); } else { const dirname = path.dirname(absPath); mkdirp.sync(dirname); yield fs.writeFile(absPath, change.contents); if (change.isExecutable) { yield fs.chmod(absPath, "755"); } } } } return repo; });
.then(function() { return git.Rebase.open(repo, {}); }).then(function(rebase) {