New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
checkout removes staged files. #1741
Comments
Seems that the relevant lines are here where This behavior seems pretty intentional so I'll need to look into it a bit more before making changes. |
For now we can read the blob from Index as shown here, then replace the content on working directory: I've edited the snippet to return a blob. const readContentsFromHash = async (hash, gitDir, filePath = null) => {
let config = {
fs,
dir: gitDir,
oid: hash,
};
if (filePath) {
config = {
...config,
filepath: filePath,
};
}
const { blob } = await git.readBlob(config);
// return new TextDecoder().decode(blob);
return blob;
};
const getStagedFileContents = async (gitDir, stagedFilePaths) => {
const map = async (filePath, [A]) => {
if (stagedFilePaths.includes(filePath)) {
const contents = await readContentsFromHash(await A.oid(), gitDir);
return {
filePath: `/${filePath}`,
contents,
};
}
};
return await git.walk({
fs,
dir: gitDir,
trees: [git.STAGE()],
map,
});
};
// get content from staging
let items = await getStagedFileContents('/', ['style.css'])
// returns [ {filePath: '/style.css', contents: Uint8Array(4)} ]
// update the working directory
let newContent = items[0].contents;
await fs.promises.writeFile('/style.css', newContent); |
I think that I've missed this issue. Can you show an example of canonical git sequence of commands and how it differs from isomorphic-git? It will help to understand if this is a bug or expected behavior. |
Suppose I have style.css in HEAD:
In canonical git, style.css content is expected to be restored to INDEX (step 2), but isomorphic-git's checkout discard the whole changes (INDEX, WORKDIR) back to HEAD. |
This is my old code that I used to checkout a single file: async function readBranchFile({ dir, filepath, branch }) {
const ref = 'refs/remotes/origin/' + branch;
const sha = await git.resolveRef({ dir, ref });
const { object: { tree } } = await git.readObject({ dir, oid: sha });
return (async function loop(tree, path) {
if (!path.length) {
throw new Error(`File ${filepath} not found`);
}
var name = path.shift();
const { object: { entries } } = await git.readObject({ dir, oid: tree });
const packageEntry = entries.find((entry) => {
return entry.path === name;
});
if (!packageEntry) {
throw new Error(`File ${filepath} not found`);
} else {
if (packageEntry.type == 'blob') {
const { object: pkg } = await git.readObject({ dir, oid: packageEntry.oid });
return pkg.toString('utf8');
} else if (packageEntry.type == 'tree') {
return loop(packageEntry.oid, path);
}
}
})(tree, filepath.split('/'));
}
function gitCheckoutFile({dir, filepath, branch}) {
var fname = dir + '/' + filepath;
return new Promise(function(resolve, reject) {
readBranchFile({dir, branch, filepath}).then(oldFile => {
if (!oldFile) {
return;
}
fs.writeFile(fname, oldFile, err => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
});
} NOTE that the code was written before version 1.0 came out. |
I didn't test the current API, but it looks like the option is Can you share how you use the API? |
@jcubic I adjust your code to v1.x and can confirm that Adjustment:
async function readBranchFile({ dir, filepath, branch }) {
// const ref = 'refs/remotes/origin/' + branch;
const ref = branch;
const sha = await git.resolveRef({ fs, dir, ref });
const { object: { tree } } = await git.readObject({ fs, dir, oid: sha });
return (async function loop(tree, path) {
if (!path.length) {
throw new Error(`File ${filepath} not found`);
}
var name = path.shift();
const result = await git.readObject({ fs, dir, oid: tree });
// console.log(result)
// const { object: { entries } } = result;
const packageEntry = result.object.find((entry) => {
return entry.path === name;
});
console.log(packageEntry)
if (!packageEntry) {
throw new Error(`File ${filepath} not found`);
} else {
if (packageEntry.type == 'blob') {
const { object: pkg } = await git.readObject({ fs, dir, oid: packageEntry.oid });
// return pkg.toString('utf8');
return pkg;
} else if (packageEntry.type == 'tree') {
return loop(packageEntry.oid, path);
}
}
})(tree, filepath.split('/'));
}
function gitCheckoutFile({dir, filepath, branch}) {
var fname = dir + '/' + filepath;
return new Promise(function(resolve, reject) {
readBranchFile({dir, branch, filepath}).then(oldFile => {
if (!oldFile) {
return;
}
fs.writeFile(fname, oldFile, err => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
});
} What I, and possibly the folks on obsidian git plugin trying to do is to discard the changes on WORKDIR by running In isomorphic-git's checkout, however, the staged changes is seemingly unstaged and discarded along with changes in WORKDIR. Here's the git command to test the behaviour in canonical git: echo '' >> style.css
git add style.css
git commit -m "init"
echo 'Hello' >> style.css
git add style.css
echo ' World' >> style.css
git checkout -- style.css And here's how I use it previously to discard changes in WORKDIR (which had some unexpected behaviour when having staged changes): await git.checkout({
fs,
dir: '/',
force: true,
filepaths: ['style.css']
}) |
Ok, so this is definitely a bug. Do you want to contribute a full solution that will match the API? You will need to add unit test to that, to test your scenario. You can add unit test first to make sure it's failing without the fix. |
Not anytime soon I'm afraid. Guess I'll see if I can run the project first on Codespace. |
This plugin is used in the obsidian android git plugin. There was a bug that deleted files in certain occasions. The plugin author mentioned this is a bug with isomorphic-git. Because checkout removes staged files.
For repro, and discussion see denolehov/obsidian-git#396 (comment)
The text was updated successfully, but these errors were encountered: