in org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java [217:386]
public RevCommit call() throws GitAPIException {
checkCallable();
List<String> deletedFiles = new ArrayList<>();
Ref head = getHead();
try (ObjectReader reader = repo.newObjectReader()) {
RevCommit headCommit = parseCommit(reader, head.getObjectId());
DirCache cache = repo.lockDirCache();
ObjectId commitId;
try (ObjectInserter inserter = repo.newObjectInserter();
TreeWalk treeWalk = new TreeWalk(repo, reader)) {
treeWalk.setRecursive(true);
treeWalk.addTree(headCommit.getTree());
treeWalk.addTree(new DirCacheIterator(cache));
treeWalk.addTree(new FileTreeIterator(repo));
treeWalk.getTree(2, FileTreeIterator.class)
.setDirCacheIterator(treeWalk, 1);
treeWalk.setFilter(AndTreeFilter.create(new SkipWorkTreeFilter(
1), new IndexDiffFilter(1, 2)));
// Return null if no local changes to stash
if (!treeWalk.next())
return null;
MutableObjectId id = new MutableObjectId();
List<PathEdit> wtEdits = new ArrayList<>();
List<String> wtDeletes = new ArrayList<>();
List<DirCacheEntry> untracked = new ArrayList<>();
boolean hasChanges = false;
do {
AbstractTreeIterator headIter = treeWalk.getTree(0,
AbstractTreeIterator.class);
DirCacheIterator indexIter = treeWalk.getTree(1,
DirCacheIterator.class);
WorkingTreeIterator wtIter = treeWalk.getTree(2,
WorkingTreeIterator.class);
if (indexIter != null
&& !indexIter.getDirCacheEntry().isMerged())
throw new UnmergedPathsException(
new UnmergedPathException(
indexIter.getDirCacheEntry()));
if (wtIter != null) {
if (indexIter == null && headIter == null
&& !includeUntracked)
continue;
hasChanges = true;
if (indexIter != null && wtIter.idEqual(indexIter))
continue;
if (headIter != null && wtIter.idEqual(headIter))
continue;
treeWalk.getObjectId(id, 0);
final DirCacheEntry entry = new DirCacheEntry(
treeWalk.getRawPath());
entry.setLength(wtIter.getEntryLength());
entry.setLastModified(
wtIter.getEntryLastModifiedInstant());
entry.setFileMode(wtIter.getEntryFileMode());
long contentLength = wtIter.getEntryContentLength();
try (InputStream in = wtIter.openEntryStream()) {
entry.setObjectId(inserter.insert(
Constants.OBJ_BLOB, contentLength, in));
}
if (indexIter == null && headIter == null)
untracked.add(entry);
else
wtEdits.add(new PathEdit(entry) {
@Override
public void apply(DirCacheEntry ent) {
ent.copyMetaData(entry);
}
});
}
hasChanges = true;
if (wtIter == null && headIter != null)
wtDeletes.add(treeWalk.getPathString());
} while (treeWalk.next());
if (!hasChanges)
return null;
String branch = Repository.shortenRefName(head.getTarget()
.getName());
// Commit index changes
CommitBuilder builder = createBuilder();
builder.setParentId(headCommit);
builder.setTreeId(cache.writeTree(inserter));
builder.setMessage(MessageFormat.format(indexMessage, branch,
headCommit.abbreviate(OBJECT_ID_ABBREV_STRING_LENGTH)
.name(),
headCommit.getShortMessage()));
ObjectId indexCommit = inserter.insert(builder);
// Commit untracked changes
ObjectId untrackedCommit = null;
if (!untracked.isEmpty()) {
DirCache untrackedDirCache = DirCache.newInCore();
DirCacheBuilder untrackedBuilder = untrackedDirCache
.builder();
for (DirCacheEntry entry : untracked)
untrackedBuilder.add(entry);
untrackedBuilder.finish();
builder.setParentIds(new ObjectId[0]);
builder.setTreeId(untrackedDirCache.writeTree(inserter));
builder.setMessage(MessageFormat.format(MSG_UNTRACKED,
branch,
headCommit
.abbreviate(OBJECT_ID_ABBREV_STRING_LENGTH)
.name(),
headCommit.getShortMessage()));
untrackedCommit = inserter.insert(builder);
}
// Commit working tree changes
if (!wtEdits.isEmpty() || !wtDeletes.isEmpty()) {
DirCacheEditor editor = cache.editor();
for (PathEdit edit : wtEdits)
editor.add(edit);
for (String path : wtDeletes)
editor.add(new DeletePath(path));
editor.finish();
}
builder.setParentId(headCommit);
builder.addParentId(indexCommit);
if (untrackedCommit != null)
builder.addParentId(untrackedCommit);
builder.setMessage(MessageFormat.format(
workingDirectoryMessage, branch,
headCommit.abbreviate(OBJECT_ID_ABBREV_STRING_LENGTH)
.name(),
headCommit.getShortMessage()));
builder.setTreeId(cache.writeTree(inserter));
commitId = inserter.insert(builder);
inserter.flush();
updateStashRef(commitId, builder.getAuthor(),
builder.getMessage());
// Remove untracked files
if (includeUntracked) {
for (DirCacheEntry entry : untracked) {
String repoRelativePath = entry.getPathString();
File file = new File(repo.getWorkTree(),
repoRelativePath);
FileUtils.delete(file);
deletedFiles.add(repoRelativePath);
}
}
} finally {
cache.unlock();
}
// Hard reset to HEAD
new ResetCommand(repo).setMode(ResetType.HARD).call();
// Return stashed commit
return parseCommit(reader, commitId);
} catch (IOException e) {
throw new JGitInternalException(JGitText.get().stashFailed, e);
} finally {
if (!deletedFiles.isEmpty()) {
repo.fireEvent(
new WorkingTreeModifiedEvent(null, deletedFiles));
}
}
}