gitdirector 1.2.0__tar.gz → 1.2.2__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.
Files changed (23) hide show
  1. {gitdirector-1.2.0 → gitdirector-1.2.2}/PKG-INFO +1 -1
  2. {gitdirector-1.2.0 → gitdirector-1.2.2}/pyproject.toml +1 -1
  3. {gitdirector-1.2.0 → gitdirector-1.2.2}/src/gitdirector/manager.py +2 -2
  4. {gitdirector-1.2.0 → gitdirector-1.2.2}/src/gitdirector/repo.py +65 -40
  5. {gitdirector-1.2.0 → gitdirector-1.2.2}/README.md +0 -0
  6. {gitdirector-1.2.0 → gitdirector-1.2.2}/src/gitdirector/__init__.py +0 -0
  7. {gitdirector-1.2.0 → gitdirector-1.2.2}/src/gitdirector/cli.py +0 -0
  8. {gitdirector-1.2.0 → gitdirector-1.2.2}/src/gitdirector/commands/__init__.py +0 -0
  9. {gitdirector-1.2.0 → gitdirector-1.2.2}/src/gitdirector/commands/autoclean.py +0 -0
  10. {gitdirector-1.2.0 → gitdirector-1.2.2}/src/gitdirector/commands/cd.py +0 -0
  11. {gitdirector-1.2.0 → gitdirector-1.2.2}/src/gitdirector/commands/help.py +0 -0
  12. {gitdirector-1.2.0 → gitdirector-1.2.2}/src/gitdirector/commands/link.py +0 -0
  13. {gitdirector-1.2.0 → gitdirector-1.2.2}/src/gitdirector/commands/listt.py +0 -0
  14. {gitdirector-1.2.0 → gitdirector-1.2.2}/src/gitdirector/commands/pull.py +0 -0
  15. {gitdirector-1.2.0 → gitdirector-1.2.2}/src/gitdirector/commands/status.py +0 -0
  16. {gitdirector-1.2.0 → gitdirector-1.2.2}/src/gitdirector/commands/tui/__init__.py +0 -0
  17. {gitdirector-1.2.0 → gitdirector-1.2.2}/src/gitdirector/commands/tui/app.py +0 -0
  18. {gitdirector-1.2.0 → gitdirector-1.2.2}/src/gitdirector/commands/tui/constants.py +0 -0
  19. {gitdirector-1.2.0 → gitdirector-1.2.2}/src/gitdirector/commands/tui/screens.py +0 -0
  20. {gitdirector-1.2.0 → gitdirector-1.2.2}/src/gitdirector/commands/unlink.py +0 -0
  21. {gitdirector-1.2.0 → gitdirector-1.2.2}/src/gitdirector/config.py +0 -0
  22. {gitdirector-1.2.0 → gitdirector-1.2.2}/src/gitdirector/integrations/__init__.py +0 -0
  23. {gitdirector-1.2.0 → gitdirector-1.2.2}/src/gitdirector/integrations/tmux.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: gitdirector
3
- Version: 1.2.0
3
+ Version: 1.2.2
4
4
  Summary: A terminal based control plane for developers working across multiple repositories. Launch multiple AI coding agents, multiple tmux sessions and track changes across all your repos in one place.
5
5
  Keywords: git,repository,manager,cli,synchronization,batch
6
6
  Author: Anito Anto
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "gitdirector"
7
- version = "1.2.0"
7
+ version = "1.2.2"
8
8
  description = "A terminal based control plane for developers working across multiple repositories. Launch multiple AI coding agents, multiple tmux sessions and track changes across all your repos in one place."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -131,11 +131,11 @@ class RepositoryManager:
131
131
  except Exception as e:
132
132
  return False, f"Error removing repositories: {str(e)}", []
133
133
 
134
- def get_repository_status(self, path: Path) -> RepositoryInfo:
134
+ def get_repository_status(self, path: Path, *, fetch: bool = False) -> RepositoryInfo:
135
135
  if path.exists() and (path / ".git").is_dir():
136
136
  try:
137
137
  repo = Repository(path)
138
- return repo.get_status()
138
+ return repo.get_status(fetch=fetch)
139
139
  except Exception as e:
140
140
  return RepositoryInfo(path, path.name, RepoStatus.UNKNOWN, None, str(e))
141
141
  return RepositoryInfo(
@@ -125,54 +125,79 @@ class Repository:
125
125
  pass
126
126
  return total
127
127
 
128
- def get_status(self) -> RepositoryInfo:
129
- branch = self.get_current_branch()
130
-
131
- code, out, err = self._run_git("fetch")
132
- if code != 0:
133
- return RepositoryInfo(self.path, self.name, RepoStatus.UNKNOWN, branch, err)
134
-
135
- code, ahead_behind, _ = self._run_git("rev-list", "--left-right", "--count", "@{u}...HEAD")
136
-
128
+ def get_status(self, *, fetch: bool = False) -> RepositoryInfo:
129
+ if fetch:
130
+ code, _, err = self._run_git("fetch")
131
+ if code != 0:
132
+ branch = self.get_current_branch()
133
+ return RepositoryInfo(self.path, self.name, RepoStatus.UNKNOWN, branch, err)
134
+
135
+ code, out, _ = self._run_git("status", "--porcelain=v2", "--branch", _strip=False)
137
136
  if code != 0:
138
137
  return RepositoryInfo(
139
- self.path, self.name, RepoStatus.UNKNOWN, branch, "No tracking branch"
138
+ self.path, self.name, RepoStatus.UNKNOWN, None, "git status failed"
140
139
  )
141
140
 
142
- try:
143
- behind, ahead = map(int, ahead_behind.split())
144
- if ahead > 0 and behind > 0:
145
- status = RepoStatus.DIVERGED
146
- msg = f"ahead {ahead}, behind {behind}"
147
- elif ahead > 0:
148
- status = RepoStatus.AHEAD
149
- msg = f"ahead {ahead}"
150
- elif behind > 0:
151
- status = RepoStatus.BEHIND
152
- msg = f"behind {behind}"
153
- else:
154
- status = RepoStatus.UP_TO_DATE
155
- msg = ""
156
- except ValueError:
157
- status = RepoStatus.UNKNOWN
158
- msg = "Could not parse git status"
159
-
160
- code, porcelain, _ = self._run_git("status", "--porcelain", _strip=False)
141
+ branch = None
142
+ has_upstream = False
143
+ ahead = 0
144
+ behind = 0
161
145
  staged = False
162
146
  unstaged = False
163
147
  staged_files: list[str] = []
164
148
  unstaged_files: list[str] = []
165
- if code == 0 and porcelain:
166
- for line in porcelain.splitlines():
167
- if len(line) >= 2:
168
- x, y = line[0], line[1]
169
- filename = line[3:].strip()
170
- if x not in (" ", "?"):
171
- staged = True
172
- staged_files.append(filename)
173
- if y not in (" ", "?"):
174
- unstaged = True
175
- unstaged_files.append(filename)
149
+
150
+ for line in out.splitlines():
151
+ if line.startswith("# branch.head "):
152
+ branch = line[14:]
153
+ if branch == "(detached)":
154
+ branch = None
155
+ elif line.startswith("# branch.ab "):
156
+ has_upstream = True
157
+ parts = line.split()
158
+ try:
159
+ ahead = int(parts[2].lstrip("+"))
160
+ behind = abs(int(parts[3]))
161
+ except (IndexError, ValueError):
162
+ pass
163
+ elif line.startswith("1 ") or line.startswith("2 "):
164
+ xy = line[2:4]
165
+ x, y = xy[0], xy[1]
166
+ if line.startswith("1 "):
167
+ parts = line.split(" ", 8)
168
+ filename = parts[8] if len(parts) > 8 else ""
169
+ else:
170
+ parts = line.split(" ", 9)
171
+ filename = parts[9].split("\t")[0] if len(parts) > 9 else ""
172
+ if x not in (".", "?"):
173
+ staged = True
174
+ staged_files.append(filename)
175
+ if y not in (".", "?"):
176
+ unstaged = True
177
+ unstaged_files.append(filename)
178
+ elif line.startswith("u "):
179
+ parts = line.split(" ", 10)
180
+ filename = parts[10] if len(parts) > 10 else ""
181
+ staged = True
182
+ unstaged = True
183
+ staged_files.append(filename)
184
+ unstaged_files.append(filename)
185
+
186
+ if not has_upstream:
187
+ status = RepoStatus.UNKNOWN
188
+ msg = "No tracking branch"
189
+ elif ahead > 0 and behind > 0:
190
+ status = RepoStatus.DIVERGED
191
+ msg = f"ahead {ahead}, behind {behind}"
192
+ elif ahead > 0:
193
+ status = RepoStatus.AHEAD
194
+ msg = f"ahead {ahead}"
195
+ elif behind > 0:
196
+ status = RepoStatus.BEHIND
197
+ msg = f"behind {behind}"
198
+ else:
199
+ status = RepoStatus.UP_TO_DATE
200
+ msg = ""
176
201
 
177
202
  last_updated, last_commit_ts = self.get_last_commit_info()
178
203
  size = self.get_tracked_size()
File without changes