gitpush-tool 0.2.8__tar.gz → 0.2.10__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitpush-tool
3
- Version: 0.2.8
3
+ Version: 0.2.10
4
4
  Summary: Supercharged Git push tool with automatic GitHub repo creation and pushing
5
5
  Home-page: https://github.com/inevitablegs/gitpush
6
6
  Author: Ganesh Sonawane
@@ -20,22 +20,6 @@ A supercharged Git CLI tool that simplifies repository creation and pushing with
20
20
  pip install gitpush-tool
21
21
  ```
22
22
 
23
- ### Prerequisites
24
-
25
- - GitHub CLI (gh)
26
- - Git
27
-
28
- ```bash
29
- # Install GitHub CLI
30
- # macOS
31
- brew install gh
32
-
33
- # Windows
34
- winget install --id GitHub.cli
35
-
36
- # Linux (Debian/Ubuntu)
37
- sudo apt install gh
38
- ```
39
23
 
40
24
  ## Usage 🛠️
41
25
 
@@ -179,4 +163,4 @@ Contributions welcome! Please follow these steps:
179
163
 
180
164
  MIT - See LICENSE for details.
181
165
 
182
- <center>✨ <strong>Happy Coding!</strong> ✨</center>
166
+ <center>✨ <strong>Happy Coding!</strong> ✨</center>
@@ -0,0 +1 @@
1
+ __version__ = "0.2.10"
@@ -0,0 +1,279 @@
1
+ import os
2
+ import argparse
3
+ import sys
4
+ import subprocess
5
+ import shutil
6
+ import platform
7
+
8
+ def check_gh_installed():
9
+ """
10
+ Checks if GitHub CLI is installed. If not, it provides a comprehensive list
11
+ of platform-specific installation commands and fallbacks.
12
+ """
13
+ if shutil.which("gh"):
14
+ return True
15
+
16
+ print("❌ Error: GitHub CLI (gh) is not installed or not in your system's PATH.", file=sys.stderr)
17
+ print(" The '--new-repo' feature requires the GitHub CLI for all repository operations.", file=sys.stderr)
18
+
19
+ system = platform.system()
20
+ install_options = []
21
+
22
+ # --- Platform-specific detection with fallbacks ---
23
+ if system == "Windows":
24
+ print("\n➡️ For Windows, we recommend one of the following package managers:", file=sys.stderr)
25
+ if shutil.which("winget"):
26
+ install_options.append(("Winget (Recommended)", "winget install --id GitHub.cli --source winget"))
27
+ if shutil.which("scoop"):
28
+ install_options.append(("Scoop", "scoop install gh"))
29
+ if shutil.which("choco"):
30
+ install_options.append(("Chocolatey", "choco install gh"))
31
+
32
+ elif system == "Darwin":
33
+ print("\n➡️ For macOS, we recommend using Homebrew:", file=sys.stderr)
34
+ if shutil.which("brew"):
35
+ install_options.append(("Homebrew (Recommended)", "brew install gh"))
36
+
37
+ elif system == "Linux":
38
+ print("\n➡️ For Linux, we recommend one of the following package managers:", file=sys.stderr)
39
+ if shutil.which("apt") or shutil.which("apt-get"):
40
+ install_options.append(("Debian/Ubuntu/Mint", "sudo apt update && sudo apt install gh -y"))
41
+ if shutil.which("dnf"):
42
+ install_options.append(("Fedora/RHEL/CentOS", "sudo dnf install gh"))
43
+ if shutil.which("yum"):
44
+ install_options.append(("Older Fedora/RHEL/CentOS", "sudo yum install gh"))
45
+ if shutil.which("pacman"):
46
+ install_options.append(("Arch Linux", "sudo pacman -S github-cli"))
47
+ if shutil.which("zypper"):
48
+ install_options.append(("openSUSE", "sudo zypper install gh"))
49
+
50
+ # --- Displaying the options and the ultimate fallback ---
51
+ if install_options:
52
+ print(" Please copy and run one of the commands below in your terminal:\n", file=sys.stderr)
53
+ for name, command in install_options:
54
+ print(f" # For {name}:\n {command}\n", file=sys.stderr)
55
+
56
+ print(" If none of the above are available on your system, please follow the", file=sys.stderr)
57
+ print(" official installation guide for more options (including manual download):", file=sys.stderr)
58
+ print(" https://github.com/cli/cli#installation\n", file=sys.stderr)
59
+ print("‼️ After installation, please open a NEW terminal and run your command again.", file=sys.stderr)
60
+
61
+ return False
62
+
63
+ def gh_authenticated():
64
+ """Check if user is authenticated with GitHub CLI"""
65
+ try:
66
+ result = subprocess.run(["gh", "auth", "status"], capture_output=True, text=True, check=True)
67
+ return "Logged in to github.com" in result.stderr
68
+ except (subprocess.CalledProcessError, FileNotFoundError):
69
+ return False
70
+
71
+ def authenticate_with_gh():
72
+ """Authenticate user with GitHub CLI"""
73
+ print("\n🔑 GitHub authentication required.")
74
+ print("The tool will use the GitHub CLI (gh) to open a browser for secure login.")
75
+
76
+ try:
77
+ subprocess.run(["gh", "auth", "login", "--web", "-h", "github.com"], check=True)
78
+ return True
79
+ except subprocess.CalledProcessError:
80
+ print("❌ Authentication failed. Please try running 'gh auth login' manually.", file=sys.stderr)
81
+ return False
82
+
83
+ def initialize_git_repository():
84
+ """Initialize git repository if not already initialized"""
85
+ if os.path.exists(".git"):
86
+ return False
87
+
88
+ print("🛠 Initializing git repository")
89
+ try:
90
+ subprocess.run(["git", "init"], check=True, capture_output=True)
91
+ subprocess.run(["git", "branch", "-M", "main"], check=True, capture_output=True)
92
+
93
+ if not os.path.exists(".gitignore"):
94
+ with open(".gitignore", "w") as f:
95
+ f.write("""# Python
96
+ __pycache__/
97
+ *.py[cod]
98
+ *.so
99
+ .Python
100
+ env/
101
+ venv/
102
+ .env
103
+
104
+ # IDE
105
+ .vscode/
106
+ .idea/
107
+ *.swp
108
+ *.swo
109
+
110
+ # System
111
+ .DS_Store
112
+ Thumbs.db
113
+
114
+ # Project specific
115
+ *.log
116
+ *.tmp
117
+ *.bak
118
+ """)
119
+ print("📁 Created .gitignore file")
120
+ return True
121
+ except subprocess.CalledProcessError as e:
122
+ print(f"❌ Failed to initialize Git repository: {e.stderr.decode().strip()}", file=sys.stderr)
123
+ return False
124
+
125
+ def create_initial_commit(commit_message="Initial commit"):
126
+ """Create initial commit if no commits exist"""
127
+ try:
128
+ result = subprocess.run(["git", "rev-list", "--count", "HEAD"],
129
+ capture_output=True, text=True)
130
+ commit_count = int(result.stdout.strip()) if result.stdout.strip().isdigit() else 0
131
+
132
+ if commit_count == 0:
133
+ print("📦 Creating initial commit")
134
+ subprocess.run(["git", "add", "."], check=True)
135
+ subprocess.run(["git", "commit", "-m", commit_message], check=True)
136
+ return True
137
+ return False
138
+ except subprocess.CalledProcessError as e:
139
+ if "nothing to commit" in e.stderr.decode():
140
+ print(f"❌ Failed to create initial commit: No files found to commit.", file=sys.stderr)
141
+ print("➡️ Add some files to your project directory before creating a repository.", file=sys.stderr)
142
+ else:
143
+ print(f"❌ Failed to create initial commit: {e.stderr.decode().strip()}", file=sys.stderr)
144
+ return False
145
+
146
+ def create_with_gh_cli(repo_name, private=False, description="", commit_message="Initial commit"):
147
+ """Create and push to new repository using GitHub CLI"""
148
+ try:
149
+ if not os.path.exists(".git"):
150
+ if not initialize_git_repository():
151
+ return False
152
+
153
+ if not create_initial_commit(commit_message):
154
+ if subprocess.run(["git", "status"], capture_output=True).returncode != 0:
155
+ return False
156
+ print("ℹ️ Using existing commits")
157
+
158
+ private_flag = "--private" if private else "--public"
159
+ cmd = ["gh", "repo", "create", repo_name, private_flag,
160
+ "--source=.", "--remote=origin", "--push"]
161
+
162
+ if description:
163
+ cmd.extend(["--description", description])
164
+
165
+ print("🚀 Creating repository and pushing code...")
166
+ process = subprocess.run(cmd, check=True, capture_output=True, text=True)
167
+
168
+ repo_url = process.stderr.strip()
169
+ print(f"✅ Successfully created repository: {repo_url}")
170
+ return True
171
+
172
+ except subprocess.CalledProcessError as e:
173
+ error_message = e.stderr.strip()
174
+ if "already exists" in error_message:
175
+ print(f"❌ Failed to create repository: {error_message}", file=sys.stderr)
176
+ print("➡️ Please choose a different repository name.", file=sys.stderr)
177
+ else:
178
+ print(f"❌ Failed to create repository: {error_message}", file=sys.stderr)
179
+ return False
180
+ except Exception as e:
181
+ print(f"❌ An unexpected error occurred: {str(e)}", file=sys.stderr)
182
+ return False
183
+
184
+ def standard_git_push(commit_message, branch, remote, force=False, tags=False):
185
+ """Handle standard git push operations"""
186
+ try:
187
+ subprocess.run(["git", "add", "."], check=True)
188
+
189
+ if commit_message:
190
+ print(f"📦 Committing with message: '{commit_message}'")
191
+ subprocess.run(["git", "commit", "-m", commit_message, "--allow-empty-message"], check=True)
192
+ else:
193
+ print("ℹ️ No commit message provided. Pushing only staged changes.")
194
+
195
+ push_cmd = ["git", "push"]
196
+ if force:
197
+ push_cmd.append("--force-with-lease")
198
+ print("⚠️ Using safe force push (--force-with-lease).")
199
+ if tags:
200
+ push_cmd.append("--tags")
201
+ if remote and branch:
202
+ push_cmd.extend([remote, branch])
203
+
204
+ print(f"🚀 Executing: {' '.join(push_cmd)}")
205
+ subprocess.run(push_cmd, check=True)
206
+ print("✅ Successfully pushed changes.")
207
+ return True
208
+ except subprocess.CalledProcessError as e:
209
+ error_output = e.stderr.decode().strip() if e.stderr else str(e)
210
+ if "nothing to commit" in error_output:
211
+ print("ℹ️ No changes to commit. Nothing to do.")
212
+ return True
213
+ print(f"❌ Push failed: {error_output}", file=sys.stderr)
214
+ return False
215
+
216
+ def run():
217
+ parser = argparse.ArgumentParser(
218
+ description="🚀 Supercharged Git push tool with GitHub repo creation",
219
+ formatter_class=argparse.RawDescriptionHelpFormatter,
220
+ epilog="""Examples:
221
+ Standard push: gitpush_tool "My new feature"
222
+ Create new repo: gitpush_tool "Initial commit" --new-repo my-awesome-project
223
+ Private repository: gitpush_tool "Initial commit" --new-repo my-secret-project --private
224
+ Force push (safe): gitpush_tool "Rebased feature" --force
225
+ Initialize only: gitpush_tool --init
226
+ """
227
+ )
228
+ parser.add_argument("commit", nargs="?", help="Commit message (optional if just pushing staged changes).")
229
+ parser.add_argument("branch", nargs="?", default=None, help="Branch name (defaults to current branch).")
230
+ parser.add_argument("remote", nargs="?", default="origin", help="Remote name (default: origin).")
231
+ parser.add_argument("--force", action="store_true", help="Force push with --force-with-lease.")
232
+ parser.add_argument("--tags", action="store_true", help="Push all tags.")
233
+ parser.add_argument("--init", action="store_true", help="Initialize a new Git repository and exit.")
234
+ parser.add_argument("--new-repo", metavar="REPO_NAME", help="Create a new GitHub repository with the given name.")
235
+ parser.add_argument("--private", action="store_true", help="Make the new repository private.")
236
+ parser.add_argument("--description", help="Description for the new repository.")
237
+
238
+ args = parser.parse_args()
239
+
240
+ target_branch = args.branch
241
+ if not target_branch:
242
+ try:
243
+ branch_result = subprocess.run(["git", "rev-parse", "--abbrev-ref", "HEAD"], capture_output=True, text=True, check=True)
244
+ target_branch = branch_result.stdout.strip()
245
+ except subprocess.CalledProcessError:
246
+ target_branch = "main"
247
+
248
+ if args.new_repo:
249
+ if not check_gh_installed():
250
+ sys.exit(1)
251
+
252
+ if not gh_authenticated():
253
+ if not authenticate_with_gh():
254
+ sys.exit(1)
255
+
256
+ if not create_with_gh_cli(
257
+ args.new_repo,
258
+ private=args.private,
259
+ description=args.description or "",
260
+ commit_message=args.commit or "Initial commit"
261
+ ):
262
+ sys.exit(1)
263
+
264
+ elif args.init:
265
+ if initialize_git_repository():
266
+ print("✅ Git repository initialized successfully.")
267
+
268
+ else:
269
+ if not standard_git_push(
270
+ args.commit,
271
+ target_branch,
272
+ args.remote,
273
+ args.force,
274
+ args.tags
275
+ ):
276
+ sys.exit(1)
277
+
278
+ if __name__ == "__main__":
279
+ run()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitpush-tool
3
- Version: 0.2.8
3
+ Version: 0.2.10
4
4
  Summary: Supercharged Git push tool with automatic GitHub repo creation and pushing
5
5
  Home-page: https://github.com/inevitablegs/gitpush
6
6
  Author: Ganesh Sonawane
@@ -6,7 +6,7 @@ long_description = (Path(__file__).parent / "LONG_DESCRIPTION.md").read_text(enc
6
6
 
7
7
  setup(
8
8
  name="gitpush-tool",
9
- version="0.2.8",
9
+ version="0.2.10",
10
10
  packages=find_packages(),
11
11
  install_requires=[],
12
12
  entry_points={
@@ -1 +0,0 @@
1
- __version__ = "0.2.8"
@@ -1,287 +0,0 @@
1
- import os
2
- import argparse
3
- import sys
4
- import subprocess
5
- import platform
6
- import urllib.request
7
- import tempfile
8
- import shutil
9
-
10
- def check_gh_installed():
11
- """Check if GitHub CLI is installed, attempt installation if not"""
12
- if shutil.which("gh"):
13
- return True
14
-
15
- print("📦 GitHub CLI not found. Attempting installation...")
16
- system = platform.system()
17
-
18
- try:
19
- if system == "Windows":
20
- return install_gh_cli_windows()
21
- elif system == "Darwin":
22
- subprocess.run(["brew", "install", "gh"], check=True)
23
- elif system == "Linux":
24
- subprocess.run(["sudo", "apt", "install", "-y", "gh"], check=True)
25
- else:
26
- print("❌ Unsupported OS.")
27
- return False
28
-
29
- return shutil.which("gh") is not None
30
- except Exception as e:
31
- print(f"❌ Failed to install GitHub CLI: {e}")
32
- return False
33
-
34
- import os
35
- import subprocess
36
- import shutil
37
- import tempfile
38
- import urllib.request
39
- import json
40
-
41
- def install_gh_cli_windows():
42
- """Install GitHub CLI on Windows using winget or direct download"""
43
- # Try winget first
44
- if shutil.which("winget"):
45
- try:
46
- print("📦 Attempting to install GitHub CLI with winget...")
47
- subprocess.run(["winget", "install", "--id", "GitHub.cli", "--silent"], check=True)
48
- print("✅ GitHub CLI installed successfully via winget.")
49
- return True
50
- except (subprocess.CalledProcessError, FileNotFoundError):
51
- print("⚠️ Winget installation failed. Falling back to direct download.")
52
-
53
- # Fallback to direct download
54
- try:
55
- print("⬇️ Finding the latest GitHub CLI release for Windows...")
56
- api_url = "https://api.github.com/repos/cli/cli/releases/latest"
57
- with urllib.request.urlopen(api_url) as response:
58
- data = json.loads(response.read().decode())
59
-
60
- # Find the correct MSI asset
61
- msi_url = None
62
- for asset in data.get("assets", []):
63
- if asset.get("name", "").endswith("_windows_amd64.msi"):
64
- msi_url = asset.get("browser_download_url")
65
- break
66
-
67
- if not msi_url:
68
- print("❌ Could not find a downloadable MSI file for the latest release.")
69
- return False
70
-
71
- msi_path = os.path.join(tempfile.gettempdir(), "gh_installer.msi")
72
-
73
- print(f"⬇️ Downloading GitHub CLI from: {msi_url}")
74
- urllib.request.urlretrieve(msi_url, msi_path)
75
-
76
- print("🛠️ Installing GitHub CLI...")
77
- # Use msiexec for silent installation
78
- subprocess.run(["msiexec", "/i", msi_path, "/quiet", "/norestart"], check=True)
79
-
80
- # Clean up the downloaded file
81
- os.remove(msi_path)
82
- print("✅ GitHub CLI installed successfully.")
83
- return True
84
- except Exception as e:
85
- print(f"❌ Direct installation failed: {e}")
86
- return False
87
-
88
- def gh_authenticated():
89
- """Check if user is authenticated with GitHub CLI"""
90
- try:
91
- result = subprocess.run(["gh", "auth", "status"], capture_output=True, text=True)
92
- return result.returncode == 0
93
- except:
94
- return False
95
-
96
- def authenticate_with_gh():
97
- """Authenticate user with GitHub CLI"""
98
- print("\n🔑 GitHub authentication required")
99
- print("We'll use the GitHub CLI (gh) for authentication")
100
- print("This will open your browser for secure login")
101
-
102
- try:
103
- subprocess.run(["gh", "auth", "login", "--web", "-h", "github.com"], check=True)
104
- return True
105
- except subprocess.CalledProcessError:
106
- print("❌ Authentication failed")
107
- return False
108
-
109
- def initialize_git_repository():
110
- """Initialize git repository if not already initialized"""
111
- if os.path.exists(".git"):
112
- return False
113
-
114
- print("🛠 Initializing git repository")
115
- try:
116
- subprocess.run(["git", "init"], check=True)
117
- subprocess.run(["git", "branch", "-M", "main"], check=True)
118
-
119
- if not os.path.exists(".gitignore"):
120
- with open(".gitignore", "w") as f:
121
- f.write("""# Python
122
- __pycache__/
123
- *.py[cod]
124
- *.so
125
- .Python
126
- env/
127
- venv/
128
- .env
129
-
130
- # IDE
131
- .vscode/
132
- .idea/
133
- *.swp
134
- *.swo
135
-
136
- # System
137
- .DS_Store
138
- Thumbs.db
139
-
140
- # Project specific
141
- *.log
142
- *.tmp
143
- *.bak
144
- """)
145
- print("📁 Created .gitignore file")
146
- return True
147
- except subprocess.CalledProcessError as e:
148
- print(f"❌ Failed to initialize Git repository: {e}")
149
- return False
150
-
151
- def create_initial_commit(commit_message="Initial commit"):
152
- """Create initial commit if no commits exist"""
153
- try:
154
- result = subprocess.run(["git", "rev-list", "--count", "HEAD"],
155
- capture_output=True, text=True)
156
- commit_count = int(result.stdout.strip()) if result.stdout.strip().isdigit() else 0
157
-
158
- if commit_count == 0:
159
- print("📦 Creating initial commit")
160
- subprocess.run(["git", "add", "."], check=True)
161
- subprocess.run(["git", "commit", "-m", commit_message], check=True)
162
- return True
163
- return False
164
- except subprocess.CalledProcessError as e:
165
- print(f"❌ Failed to create initial commit: {e}")
166
- return False
167
-
168
- def create_with_gh_cli(repo_name, private=False, description="", commit_message="Initial commit"):
169
- """Create and push to new repository using GitHub CLI"""
170
- try:
171
- if not os.path.exists(".git") and not initialize_git_repository():
172
- return False
173
-
174
- if not create_initial_commit(commit_message):
175
- print("ℹ️ Using existing commits")
176
-
177
- private_flag = "--private" if private else "--public"
178
- cmd = ["gh", "repo", "create", repo_name, private_flag,
179
- "--source=.", "--remote=origin", "--push"]
180
-
181
- if description:
182
- cmd.extend(["--description", description])
183
-
184
- print("🚀 Creating repository and pushing code...")
185
- subprocess.run(cmd, check=True)
186
-
187
- url_result = subprocess.run(
188
- ["gh", "repo", "view", "--json", "url", "--jq", ".url"],
189
- capture_output=True, text=True, check=True
190
- )
191
- repo_url = url_result.stdout.strip()
192
- print(f"✅ Successfully created repository: {repo_url}")
193
- return True
194
-
195
- except subprocess.CalledProcessError as e:
196
- print(f"❌ Failed to create repository: {e.stderr if e.stderr else 'Unknown error'}")
197
- return False
198
- except Exception as e:
199
- print(f"❌ Unexpected error: {str(e)}")
200
- return False
201
-
202
- def standard_git_push(commit_message, branch, remote, force=False, tags=False):
203
- """Handle standard git push operations"""
204
- try:
205
- subprocess.run(["git", "add", "."], check=True)
206
-
207
- if commit_message:
208
- print(f"📦 Committing: '{commit_message}'")
209
- subprocess.run(["git", "commit", "-m", commit_message], check=True)
210
- else:
211
- print("ℹ️ No commit message provided - skipping commit")
212
-
213
- push_cmd = ["git", "push"]
214
- if force:
215
- push_cmd.append("--force-with-lease")
216
- if tags:
217
- push_cmd.append("--tags")
218
- if remote and branch:
219
- push_cmd.extend([remote, branch])
220
-
221
- print(f"🚀 Executing: {' '.join(push_cmd)}")
222
- subprocess.run(push_cmd, check=True)
223
- print("✅ Successfully pushed changes")
224
- return True
225
- except subprocess.CalledProcessError as e:
226
- print(f"❌ Push failed: {e}")
227
- return False
228
-
229
- def run():
230
- parser = argparse.ArgumentParser(
231
- description="🚀 Supercharged Git push tool with GitHub repo creation",
232
- formatter_class=argparse.RawDescriptionHelpFormatter,
233
- epilog="""Examples:
234
- Standard push: gitpush_tool "Commit message"
235
- Create new repo: gitpush_tool "Initial commit" --new-repo project-name
236
- Private repository: gitpush_tool --new-repo private-project --private
237
- Force push: gitpush_tool "Fix critical bug" --force
238
- """
239
- )
240
- parser.add_argument("commit", nargs="?", help="Commit message")
241
- parser.add_argument("branch", nargs="?", default="main", help="Branch name (default: main)")
242
- parser.add_argument("remote", nargs="?", default="origin", help="Remote name (default: origin)")
243
- parser.add_argument("--force", action="store_true", help="Force push with --force-with-lease")
244
- parser.add_argument("--tags", action="store_true", help="Push tags")
245
- parser.add_argument("--init", action="store_true", help="Initialize git repo")
246
- parser.add_argument("--new-repo", metavar="NAME", help="Create new GitHub repository")
247
- parser.add_argument("--private", action="store_true", help="Make repository private")
248
- parser.add_argument("--description", help="Repository description")
249
-
250
- args = parser.parse_args()
251
-
252
- if args.new_repo:
253
- print(f"🆕 Creating repository: {args.new_repo}")
254
-
255
- if not check_gh_installed():
256
- print("❌ GitHub CLI (gh) is not installed")
257
- print("Please install it first:")
258
- print(" Mac (Homebrew): brew install gh")
259
- print(" Windows (Winget): winget install --id GitHub.cli")
260
- print(" Linux: See https://github.com/cli/cli#installation")
261
- sys.exit(1)
262
-
263
- if not gh_authenticated() and not authenticate_with_gh():
264
- sys.exit(1)
265
-
266
- if not create_with_gh_cli(
267
- args.new_repo,
268
- private=args.private,
269
- description=args.description or "",
270
- commit_message=args.commit or "Initial commit"
271
- ):
272
- sys.exit(1)
273
- elif args.init:
274
- if initialize_git_repository():
275
- create_initial_commit(args.commit or "Initial commit")
276
- else:
277
- if not standard_git_push(
278
- args.commit,
279
- args.branch,
280
- args.remote,
281
- args.force,
282
- args.tags
283
- ):
284
- sys.exit(1)
285
-
286
- if __name__ == "__main__":
287
- run()
File without changes
File without changes
File without changes