Skip to content

Commit cc8d1ed

Browse files
committed
Add option to include untracked files in git and mercurial fetchers
It can be confusing (especially for new users) that the git and mercurial fetchers do not include untracked files. This commit adds an option for the fetchers to include untracked files. The option is disabled by default.
1 parent 2afe2e4 commit cc8d1ed

6 files changed

Lines changed: 65 additions & 7 deletions

File tree

src/libfetchers/fetch-settings.hh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ struct FetchSettings : public Config
7070
Setting<bool> warnDirty{this, true, "warn-dirty",
7171
"Whether to warn about dirty Git/Mercurial trees."};
7272

73+
Setting<bool> includeUntrackedFiles{this, false, "include-untracked-files",
74+
"Whether to include untracked files in Git/Mercurial trees."};
75+
7376
Setting<std::string> flakeRegistry{this, "https://channels.nixos.org/flake-registry.json", "flake-registry",
7477
R"(
7578
Path or URI of the global flake registry.

src/libfetchers/git.cc

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -246,16 +246,25 @@ WorkdirInfo getWorkdirInfo(const Input & input, const Path & workdir)
246246
if (hasHead) {
247247
// Using git diff is preferrable over lower-level operations here,
248248
// because its conceptually simpler and we only need the exit code anyways.
249-
auto gitDiffOpts = Strings({ "-C", workdir, "--git-dir", gitDir, "diff", "HEAD", "--quiet"});
249+
Strings gitDiffOpts;
250+
if (fetchSettings.includeUntrackedFiles) {
251+
gitDiffOpts = Strings({ "-C", workdir, "--git-dir", gitDir, "status", "--short"});
252+
} else {
253+
gitDiffOpts = Strings({ "-C", workdir, "--git-dir", gitDir, "diff", "HEAD", "--quiet"});
254+
}
250255
if (!submodules) {
251256
// Changes in submodules should only make the tree dirty
252257
// when those submodules will be copied as well.
253258
gitDiffOpts.emplace_back("--ignore-submodules");
254259
}
255260
gitDiffOpts.emplace_back("--");
256-
runProgram("git", true, gitDiffOpts);
257261

258-
clean = true;
262+
if (fetchSettings.includeUntrackedFiles) {
263+
clean = (chomp(runProgram("git", true, gitDiffOpts)) == "");
264+
} else {
265+
runProgram("git", true, gitDiffOpts);
266+
clean = true;
267+
}
259268
}
260269
} catch (ExecError & e) {
261270
if (!WIFEXITED(e.status) || WEXITSTATUS(e.status) != 1) throw;
@@ -276,6 +285,11 @@ std::pair<StorePath, Input> fetchFromWorkdir(ref<Store> store, Input & input, co
276285
warn("Git tree '%s' is dirty", workdir);
277286

278287
auto gitOpts = Strings({ "-C", workdir, "--git-dir", gitDir, "ls-files", "-z" });
288+
if (fetchSettings.includeUntrackedFiles) {
289+
gitOpts.emplace_back("--cached");
290+
gitOpts.emplace_back("--others");
291+
gitOpts.emplace_back("--exclude-standard");
292+
}
279293
if (submodules)
280294
gitOpts.emplace_back("--recurse-submodules");
281295

src/libfetchers/mercurial.cc

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,10 @@ struct MercurialInputScheme : InputScheme
175175

176176
if (!input.getRef() && !input.getRev() && isLocal && pathExists(actualUrl + "/.hg")) {
177177

178-
bool clean = runHg({ "status", "-R", actualUrl, "--modified", "--added", "--removed" }) == "";
178+
Strings hgStatusOptions = { "status", "-R", actualUrl, "--modified", "--added", "--removed" };
179+
if (fetchSettings.includeUntrackedFiles)
180+
hgStatusOptions.push_back("--unknown");
181+
bool clean = runHg(hgStatusOptions) == "";
179182

180183
if (!clean) {
181184

@@ -190,8 +193,11 @@ struct MercurialInputScheme : InputScheme
190193

191194
input.attrs.insert_or_assign("ref", chomp(runHg({ "branch", "-R", actualUrl })));
192195

196+
hgStatusOptions = { "status", "-R", actualUrl, "--clean", "--modified", "--added", "--no-status", "--print0" };
197+
if (fetchSettings.includeUntrackedFiles)
198+
hgStatusOptions.push_back("--unknown");
193199
auto files = tokenizeString<std::set<std::string>>(
194-
runHg({ "status", "-R", actualUrl, "--clean", "--modified", "--added", "--no-status", "--print0" }), "\0"s);
200+
runHg(hgStatusOptions), "\0"s);
195201

196202
Path actualPath(absPath(actualUrl));
197203

tests/functional/fetchGit.sh

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,25 @@ path2=$(nix eval --impure --raw --expr "(builtins.fetchGit $repo).outPath")
106106
[ ! -e $path2/.git ]
107107
[[ $(cat $path2/dir1/foo) = foo ]]
108108

109+
# Using an unclean tree with --include-git-untracked-files option should yield the tracked and uncommitted changes
110+
path21=$(nix eval --include-untracked-files --impure --raw --expr "(builtins.fetchGit $repo).outPath")
111+
[ ! -e $path21/hello ]
112+
[ -e $path21/bar ]
113+
[ -e $path21/dir2/bar ]
114+
[ ! -e $path21/.git ]
115+
[[ $(cat $path21/dir1/foo) = foo ]]
116+
117+
# Using an unclean tree with --include-git-untracked-files option should take into account .gitignore files
118+
echo "/bar" >> $repo/.gitignore
119+
echo "bar" >> $repo/dir2/.gitignore
120+
path22=$(nix eval --include-untracked-files --impure --raw --expr "(builtins.fetchGit $repo).outPath")
121+
[ ! -e $path22/hello ]
122+
[ ! -e $path22/bar ]
123+
[ ! -e $path22/dir2/bar ]
124+
[ ! -e $path22/.git ]
125+
git -C $repo checkout -- $repo/.gitignore
126+
git -C $repo clean -fd
127+
109128
[[ $(nix eval --impure --raw --expr "(builtins.fetchGit $repo).rev") = 0000000000000000000000000000000000000000 ]]
110129
[[ $(nix eval --impure --raw --expr "(builtins.fetchGit $repo).dirtyRev") = "${rev2}-dirty" ]]
111130
[[ $(nix eval --impure --raw --expr "(builtins.fetchGit $repo).dirtyShortRev") = "${rev2:0:7}-dirty" ]]

tests/functional/fetchMercurial.sh

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
source common.sh
22

3-
[[ $(type -p hq) ]] || skipTest "Mercurial not installed"
3+
[[ $(type -p hg) ]] || skipTest "Mercurial not installed"
44

55
clearStore
66

@@ -88,6 +88,22 @@ path2=$(nix eval --impure --raw --expr "(builtins.fetchMercurial $repo).outPath"
8888
[ ! -e $path2/.hg ]
8989
[[ $(cat $path2/dir1/foo) = foo ]]
9090

91+
path21=$(nix eval --include-untracked-files --impure --raw --expr "(builtins.fetchMercurial $repo).outPath")
92+
[ ! -e $path21/hello ]
93+
[ -e $path21/bar ]
94+
[ -e $path21/dir2/bar ]
95+
[ ! -e $path21/.hg ]
96+
[[ $(cat $path21/dir1/foo) = foo ]]
97+
98+
echo "bar" >> $repo/.hgignore
99+
path22=$(nix eval --include-untracked-files --impure --raw --expr "(builtins.fetchMercurial $repo).outPath")
100+
[ ! -e $path22/hello ]
101+
[ ! -e $path22/bar ]
102+
[ ! -e $path22/dir2/bar ]
103+
[ ! -e $path22/.git ]
104+
sed -i '/bar/d' $repo/.hgignore
105+
hg status --cwd $repo
106+
91107
[[ $(nix eval --impure --raw --expr "(builtins.fetchMercurial $repo).rev") = 0000000000000000000000000000000000000000 ]]
92108

93109
# ... unless we're using an explicit ref.

tests/functional/flakes/mercurial.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
source ./common.sh
22

3-
[[ $(type -p hq) ]] || skipTest "Mercurial not installed"
3+
[[ $(type -p hg) ]] || skipTest "Mercurial not installed"
44

55
flake1Dir=$TEST_ROOT/flake-hg1
66
mkdir -p $flake1Dir

0 commit comments

Comments
 (0)